diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/AnnotationsTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/AnnotationsTests.cs index 770143044..3ab69d618 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/AnnotationsTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/AnnotationsTests.cs @@ -38,5 +38,39 @@ public async Task When_array_property_is_not_nullable_then_it_does_not_have_a_se await VerifyHelper.Verify(code); CSharpCompiler.AssertCompile(code); } + + [Fact] + public async Task When_custom_class_and_property_annotation_templates_are_used_then_annotations_are_on_separate_lines() + { + // Arrange + var json = @"{ 'properties': { 'name': { 'type': 'string' } } }"; + var schema = await JsonSchema.FromJsonAsync(json); + + var tempDir = Path.Combine(Path.GetTempPath(), "NJsonSchema_AnnotationTest_" + Guid.NewGuid()); + Directory.CreateDirectory(tempDir); + try + { + File.WriteAllText(Path.Combine(tempDir, "Class.Annotations.liquid"), "[System.Runtime.Serialization.DataContract]"); + File.WriteAllText(Path.Combine(tempDir, "Class.Property.Annotations.liquid"), "[System.Runtime.Serialization.DataMember]"); + + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + ClassStyle = CSharpClassStyle.Poco, + Namespace = "TestNs", + TemplateDirectory = tempDir + }); + + // Act + var code = generator.GenerateFile("MyClass"); + + // Assert - annotations should be on their own lines, not on the same line as class/property declarations + Assert.Contains("[System.Runtime.Serialization.DataContract]\n public partial class MyClass", code); + Assert.Contains("[System.Runtime.Serialization.DataMember]\n public string Name", code); + } + finally + { + Directory.Delete(tempDir, true); + } + } } } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid index 79691a587..d8c2cf9fa 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Class.liquid @@ -34,7 +34,7 @@ {%- if IsDeprecated -%} [System.Obsolete{% if HasDeprecatedMessage %}({{ DeprecatedMessage | literal }}){% endif %}] {% endif -%} -{%- template Class.Annotations -%} +{%- template Class.Annotations %} {{ TypeAccessModifier }} {% if IsAbstract %}abstract {% endif %}partial {{ ClassType }} {{ClassName}} {%- template Class.Inheritance %} { {%- if IsTuple -%} @@ -106,7 +106,7 @@ {%- if property.IsDeprecated -%} [System.Obsolete{% if property.HasDeprecatedMessage %}({{ property.DeprecatedMessage | literal }}){% endif %}] {%- endif -%} - {%- template Class.Property.Annotations -%} + {%- template Class.Property.Annotations %} public {% if UseRequiredKeyword and property.IsRequired %}required {% endif %}{{ property.Type }} {{ property.PropertyName }}{% if RenderInpc == false and RenderPrism == false %} { get; {% if property.HasSetter and RenderRecord == false %}{{ WriteAccessor }}; {% elsif RenderRecord and GenerateNativeRecords %}init; {% endif %}}{% if property.HasDefaultValue and RenderRecord == false and UseRequiredKeyword == false %} = {{ property.DefaultValue }};{% elsif GenerateNullableReferenceTypes and RenderRecord == false and UseRequiredKeyword == false %} = default!;{% endif %} {%- else -%} { diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid index 5f89e3173..d6b3cbdd9 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid @@ -7,7 +7,7 @@ {%- if IsEnumAsBitFlags -%} [System.Flags] {%- endif -%} -{%- template Enum.Annotations -%} +{%- template Enum.Annotations %} {{ TypeAccessModifier }} enum {{ Name }}{%- if HasExtendedValueRange %} : long{% endif %} { {%- for enum in Enums %} diff --git a/src/NJsonSchema.CodeGeneration/DefaultTemplateFactory.cs b/src/NJsonSchema.CodeGeneration/DefaultTemplateFactory.cs index 98674a333..958565d3b 100644 --- a/src/NJsonSchema.CodeGeneration/DefaultTemplateFactory.cs +++ b/src/NJsonSchema.CodeGeneration/DefaultTemplateFactory.cs @@ -261,7 +261,8 @@ public string Render() var withoutEmptyWhiteSpace = _emptyTemplateCleanupRegex.Replace(trimmed, string.Empty); // just to make sure we don't leak out marker - return withoutEmptyWhiteSpace.Replace("__EMPTY-TEMPLATE__", ""); + // also handle case where empty annotation template marker is on its own line (e.g. when annotation templates are empty) + return withoutEmptyWhiteSpace.Replace("\n__EMPTY-TEMPLATE__\n", "\n").Replace("__EMPTY-TEMPLATE__", ""); } catch (Exception exception) {