diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs index b22267be8..61d1e93e2 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/EnumTests.cs @@ -181,6 +181,27 @@ public async Task When_enum_list_uses_string_enums_then_ItemConverterType_is_set CSharpCompiler.AssertCompile(code); } + [Fact] + public async Task When_enum_list_uses_string_enums_then_ItemConverterType_is_set_for_STJ_target() + { + // Arrange + var schema = NewtonsoftJsonSchemaGenerator.FromType(); + var data = schema.ToJson(); + var generator = + new CSharpGenerator(schema, new CSharpGeneratorSettings + { + ClassStyle = CSharpClassStyle.Poco, + JsonLibrary = CSharpJsonLibrary.SystemTextJson + }); + + // Act + var code = generator.GenerateFile(); + + // Assert + await VerifyHelper.Verify(code); + CSharpCompiler.AssertCompile(code); + } + [Fact] public async Task When_enum_is_nullable_then_StringEnumConverter_is_set() { diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_is_string_then_generic_StringEnumConverter_is_used_nullable=False.verified.txt b/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_is_string_then_generic_StringEnumConverter_is_used_nullable=False.verified.txt index b93b79c3c..53a53cc43 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_is_string_then_generic_StringEnumConverter_is_used_nullable=False.verified.txt +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_is_string_then_generic_StringEnumConverter_is_used_nullable=False.verified.txt @@ -20,15 +20,15 @@ namespace MyNamespace public enum MyClassSize { - [System.Text.Json.Serialization.JsonStringEnumMemberName(@"small")] + [System.Runtime.Serialization.EnumMember(Value = @"small")] Small = 0, - [System.Text.Json.Serialization.JsonStringEnumMemberName(@"medium")] + [System.Runtime.Serialization.EnumMember(Value = @"medium")] Medium = 1, - [System.Text.Json.Serialization.JsonStringEnumMemberName(@"large")] + [System.Runtime.Serialization.EnumMember(Value = @"large")] Large = 2, diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_is_string_then_generic_StringEnumConverter_is_used_nullable=True.verified.txt b/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_is_string_then_generic_StringEnumConverter_is_used_nullable=True.verified.txt index bdd85fc3e..d44367a54 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_is_string_then_generic_StringEnumConverter_is_used_nullable=True.verified.txt +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_is_string_then_generic_StringEnumConverter_is_used_nullable=True.verified.txt @@ -20,15 +20,15 @@ namespace MyNamespace public enum MyClassSize { - [System.Text.Json.Serialization.JsonStringEnumMemberName(@"small")] + [System.Runtime.Serialization.EnumMember(Value = @"small")] Small = 0, - [System.Text.Json.Serialization.JsonStringEnumMemberName(@"medium")] + [System.Runtime.Serialization.EnumMember(Value = @"medium")] Medium = 1, - [System.Text.Json.Serialization.JsonStringEnumMemberName(@"large")] + [System.Runtime.Serialization.EnumMember(Value = @"large")] Large = 2, diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_list_uses_string_enums_then_ItemConverterType_is_set_for_STJ_target.verified.txt b/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_list_uses_string_enums_then_ItemConverterType_is_set_for_STJ_target.verified.txt new file mode 100644 index 000000000..5b92ff2af --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/Snapshots/EnumTests.When_enum_list_uses_string_enums_then_ItemConverterType_is_set_for_STJ_target.verified.txt @@ -0,0 +1,36 @@ +//---------------------- +// +// +//---------------------- + + +namespace MyNamespace +{ + #pragma warning disable // Disable all warnings + + public enum MyStringEnum + { + + [System.Runtime.Serialization.EnumMember(Value = @"Foo")] + Foo = 0, + + + [System.Runtime.Serialization.EnumMember(Value = @"Bar")] + Bar = 1, + + + } + + public partial class MyStringEnumListTest + { + + [System.Text.Json.Serialization.JsonPropertyName("Enums")] + // TODO(system.text.json): Add ItemConverterType with enum converter when supported + public System.Collections.Generic.ICollection Enums { get; set; } + + [System.Text.Json.Serialization.JsonPropertyName("NullableEnum")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))] + public MyStringEnum? NullableEnum { get; set; } + + } +} \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs index 0a03e71e6..172a75901 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGeneratorSettings.cs @@ -120,6 +120,9 @@ public CSharpGeneratorSettings() /// Gets or sets the CSharp JSON library to use (default: 'NewtonsoftJson', 'SystemTextJson' is experimental/not complete). public CSharpJsonLibrary JsonLibrary { get; set; } + /// Gets or sets the CSharp JSON library version to use (applies only to System.Text.Json, default: 8.0). + public decimal JsonLibraryVersion { get; set; } = 8.0m; + /// Gets or sets the CSharp JSON polymorphic serialization style (default: 'NJsonSchema', 'SystemTextJson' is experimental/not complete). public CSharpJsonPolymorphicSerializationStyle JsonPolymorphicSerializationStyle { get; set; } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Models/EnumTemplateModel.cs b/src/NJsonSchema.CodeGeneration.CSharp/Models/EnumTemplateModel.cs index 0334e5d05..f11b33b20 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Models/EnumTemplateModel.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/Models/EnumTemplateModel.cs @@ -55,6 +55,9 @@ public EnumTemplateModel(string typeName, JsonSchema schema, CSharpGeneratorSett /// Gets a value indicating whether to use System.Text.Json public bool UseSystemTextJson => _settings.JsonLibrary == CSharpJsonLibrary.SystemTextJson; + /// Gets or sets the CSharp JSON library version to use. + public decimal JsonLibraryVersion => _settings.JsonLibraryVersion; + /// Gets a value indicating whether the enum needs another base type to representing an extended value range. public bool HasExtendedValueRange => _schema.Format == JsonFormatStrings.Long; diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid index fd63e5e76..d43285703 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid @@ -62,7 +62,7 @@ {%- if UseSystemTextJson -%} [System.Text.Json.Serialization.JsonPropertyName("{{ property.Name }}")] {%- if property.IsStringEnumArray -%} - // TODO(system.text.json): Add string enum item converter + // TODO(system.text.json): Add ItemConverterType with enum converter when supported {%- endif -%} {%- else -%} [Newtonsoft.Json.JsonProperty("{{ property.Name }}", Required = {{ property.JsonPropertyRequiredCode }}{% if property.IsStringEnumArray %}, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter){% endif %})] diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid index fad6356a7..5f89e3173 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid @@ -13,7 +13,11 @@ {%- for enum in Enums %} {%- if IsStringEnum -%} {%- if UseSystemTextJson -%} +{%- if JsonLibraryVersion >= 9.0 -%} [System.Text.Json.Serialization.JsonStringEnumMemberName(@"{{ enum.Value | replace: '"', '""' }}")] +{%- else -%} + [System.Runtime.Serialization.EnumMember(Value = @"{{ enum.Value | replace: '"', '""' }}")] +{%- endif -%} {%- else -%} [System.Runtime.Serialization.EnumMember(Value = @"{{ enum.Value | replace: '"', '""' }}")] {%- endif -%} diff --git a/src/NJsonSchema.CodeGeneration.Tests/EnumGenerationTests.cs b/src/NJsonSchema.CodeGeneration.Tests/EnumGenerationTests.cs index fae470e4e..e93a65017 100644 --- a/src/NJsonSchema.CodeGeneration.Tests/EnumGenerationTests.cs +++ b/src/NJsonSchema.CodeGeneration.Tests/EnumGenerationTests.cs @@ -52,7 +52,7 @@ public async Task When_export_types_is_true_add_export_before_enum_in_typescript var schema = NewtonsoftJsonSchemaGenerator.FromType(new NewtonsoftJsonSchemaGeneratorSettings()); var data = schema.ToJson(); - TypeScriptGeneratorSettings typeScriptGeneratorSettings = new TypeScriptGeneratorSettings() + TypeScriptGeneratorSettings typeScriptGeneratorSettings = new TypeScriptGeneratorSettings { ExportTypes = true }; @@ -72,8 +72,8 @@ public async Task When_add_export_keyword_is_false_dont_add_export_before_enum_i // Arrange var schema = NewtonsoftJsonSchemaGenerator.FromType(new NewtonsoftJsonSchemaGeneratorSettings()); var data = schema.ToJson(); - - TypeScriptGeneratorSettings typeScriptGeneratorSettings = new TypeScriptGeneratorSettings() + + TypeScriptGeneratorSettings typeScriptGeneratorSettings = new TypeScriptGeneratorSettings { ExportTypes = false }; @@ -144,6 +144,30 @@ public async Task When_enum_has_string_value_then_CS_code_has_EnumMember_attribu await VerifyHelper.Verify(code); CSharpCompiler.AssertCompile(code); } + +#if NET9_0_OR_GREATER + + [Fact] + public async Task When_enum_has_string_value_then_CS_code_has_JsonStringEnumMemberName_attribute() + { + // Arrange + var schema = NewtonsoftJsonSchemaGenerator.FromType(); + var schemaData = schema.ToJson(); + + // Act + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + JsonLibraryVersion = 9.0m, + JsonLibrary = CSharpJsonLibrary.SystemTextJson + }); + var code = generator.GenerateFile("MyClass"); + + // Assert + await VerifyHelper.Verify(code); + CSharpCompiler.AssertCompile(code); + } + +#endif [Fact] public async Task When_enum_has_string_value_then_TS_code_has_string_value() diff --git a/src/NJsonSchema.CodeGeneration.Tests/NJsonSchema.CodeGeneration.Tests.csproj b/src/NJsonSchema.CodeGeneration.Tests/NJsonSchema.CodeGeneration.Tests.csproj index ac59389bc..f9bc42db2 100644 --- a/src/NJsonSchema.CodeGeneration.Tests/NJsonSchema.CodeGeneration.Tests.csproj +++ b/src/NJsonSchema.CodeGeneration.Tests/NJsonSchema.CodeGeneration.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net8.0;net9.0 $(TargetFrameworks);net472 false $(NoWarn),1998,1591,618,IDE1006 diff --git a/src/NJsonSchema.CodeGeneration.Tests/Snapshots/EnumGenerationTests.When_enum_has_string_value_then_CS_code_has_JsonStringEnumMemberName_attribute.verified.txt b/src/NJsonSchema.CodeGeneration.Tests/Snapshots/EnumGenerationTests.When_enum_has_string_value_then_CS_code_has_JsonStringEnumMemberName_attribute.verified.txt new file mode 100644 index 000000000..594965f89 --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.Tests/Snapshots/EnumGenerationTests.When_enum_has_string_value_then_CS_code_has_JsonStringEnumMemberName_attribute.verified.txt @@ -0,0 +1,32 @@ +//---------------------- +// +// +//---------------------- + + +namespace MyNamespace +{ + #pragma warning disable // Disable all warnings + + public enum StringEnum + { + + [System.Text.Json.Serialization.JsonStringEnumMemberName(@"0562")] + _0562 = 0, + + + [System.Text.Json.Serialization.JsonStringEnumMemberName(@"0532")] + _0532 = 1, + + + } + + public partial class MyClass + { + + [System.Text.Json.Serialization.JsonPropertyName("Bar")] + [System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))] + public StringEnum Bar { get; set; } + + } +} \ No newline at end of file diff --git a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenIntegerEnumUseEnumNameAttributes_ThenTheyAreIgnored.verified.txt b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenIntegerEnumUseEnumNameAttributes_ThenTheyAreIgnored.verified.txt new file mode 100644 index 000000000..bb397e7d0 --- /dev/null +++ b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenIntegerEnumUseEnumNameAttributes_ThenTheyAreIgnored.verified.txt @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "IntegerEnumContainer", + "type": "object", + "additionalProperties": false, + "properties": { + "IntegerCloudCover": { + "$ref": "#/definitions/IntegerCloudCover" + } + }, + "definitions": { + "IntegerCloudCover": { + "type": "integer", + "description": "", + "x-enumNames": [ + "Clear", + "Partial", + "Overcast" + ], + "enum": [ + 0, + 1, + 2 + ] + } + } +} \ No newline at end of file diff --git a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenStringEnumUsesEnumMemberAttribute_ThenItIsUsed.verified.txt b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenStringEnumUsesEnumMemberAttribute_ThenItIsUsed.verified.txt new file mode 100644 index 000000000..936f672bc --- /dev/null +++ b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenStringEnumUsesEnumMemberAttribute_ThenItIsUsed.verified.txt @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "StringEnumMemberContainer", + "type": "object", + "additionalProperties": false, + "properties": { + "CloudCover": { + "$ref": "#/definitions/CloudCoverWithEnumMember" + } + }, + "definitions": { + "CloudCoverWithEnumMember": { + "type": "string", + "description": "", + "x-enumNames": [ + "Clear", + "Partial", + "Overcast" + ], + "enum": [ + "Clear", + "Partly cloudy", + "Overcast" + ] + } + } +} \ No newline at end of file diff --git a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenStringEnumUsesJsonStringEnumMemberName_ThenItIsUsed.verified.txt b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenStringEnumUsesJsonStringEnumMemberName_ThenItIsUsed.verified.txt new file mode 100644 index 000000000..78547eac4 --- /dev/null +++ b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.WhenStringEnumUsesJsonStringEnumMemberName_ThenItIsUsed.verified.txt @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "StringJsonStringEnumMemberNameContainer", + "type": "object", + "additionalProperties": false, + "properties": { + "CloudCover": { + "$ref": "#/definitions/CloudCoverWithJsonStringEnumMemberName" + } + }, + "definitions": { + "CloudCoverWithJsonStringEnumMemberName": { + "type": "string", + "description": "", + "x-enumNames": [ + "Clear", + "Partial", + "Overcast" + ], + "enum": [ + "Clear", + "Partly cloudy", + "Overcast" + ] + } + } +} \ No newline at end of file diff --git a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.cs b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.cs new file mode 100644 index 000000000..a7cee7835 --- /dev/null +++ b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonEnumTests.cs @@ -0,0 +1,75 @@ +using System.Runtime.Serialization; +using System.Text.Json.Serialization; + +namespace NJsonSchema.Tests.Generation.SystemTextJson; + +public class SystemTextJsonEnumTests +{ +#if NET9_0_OR_GREATER + + public class StringJsonStringEnumMemberNameContainer + { + public CloudCoverWithJsonStringEnumMemberName CloudCover { get; set; } + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum CloudCoverWithJsonStringEnumMemberName + { + Clear, + [JsonStringEnumMemberName("Partly cloudy")] + Partial, + Overcast + } + + [Fact] + public Task WhenStringEnumUsesJsonStringEnumMemberName_ThenItIsUsed() + { + var schema = JsonSchema.FromType(); + return Verify(schema.ToJson()); // should have "Partly cloudy" in "enum" but "Partial" in "x-enumNames" + } + + public class StringEnumMemberContainer + { + public CloudCoverWithEnumMember CloudCover { get; set; } + } + + [JsonConverter(typeof(JsonStringEnumConverter))] + public enum CloudCoverWithEnumMember + { + Clear, + [EnumMember(Value = "Partly cloudy")] + Partial, + Overcast + } + + [Fact] + public Task WhenStringEnumUsesEnumMemberAttribute_ThenItIsUsed() + { + var schema = JsonSchema.FromType(); + return Verify(schema.ToJson()); // should have "Partly cloudy" in "enum" but "Partial" in "x-enumNames" + } + + public class IntegerEnumContainer + { + public IntegerCloudCover IntegerCloudCover { get; set; } + } + + // [JsonConverter(typeof(JsonStringEnumConverter))] ==> no converter + public enum IntegerCloudCover + { + Clear, + [EnumMember(Value = "Partly cloudy")] + [JsonStringEnumMemberName("Partly cloudy")] + Partial, + Overcast + } + + [Fact] + public Task WhenIntegerEnumUseEnumNameAttributes_ThenTheyAreIgnored() + { + var schema = JsonSchema.FromType(); + return Verify(schema.ToJson()); // should have 1 and no "Partly cloudy" + } + +#endif +} \ No newline at end of file diff --git a/src/NJsonSchema.Tests/NJsonSchema.Tests.csproj b/src/NJsonSchema.Tests/NJsonSchema.Tests.csproj index 08d1447c8..9d581ab1b 100644 --- a/src/NJsonSchema.Tests/NJsonSchema.Tests.csproj +++ b/src/NJsonSchema.Tests/NJsonSchema.Tests.csproj @@ -1,7 +1,7 @@ - net8.0 + net8.0;net9.0 $(TargetFrameworks);net472 false disable