Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added setting UsePolymorphicSerialization #462

Merged
merged 5 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 76 additions & 23 deletions src/Refitter.Core/CSharpClientGeneratorFactory.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using System.Reflection;
using NJsonSchema.CodeGeneration;
using NJsonSchema.CodeGeneration.CSharp;

using NSwag;
Expand All @@ -17,30 +19,43 @@ public CustomCSharpClientGenerator Create()
}
}

var csharpClientGeneratorSettings = new CSharpClientGeneratorSettings
{
GenerateClientClasses = false,
GenerateDtoTypes = true,
GenerateClientInterfaces = false,
GenerateExceptionClasses = false,
CodeGeneratorSettings = { PropertyNameGenerator = new CustomCSharpPropertyNameGenerator(), },
CSharpGeneratorSettings =
{
Namespace = settings.ContractsNamespace ?? settings.Namespace,
JsonLibrary = CSharpJsonLibrary.SystemTextJson,
TypeAccessModifier = settings.TypeAccessibility.ToString().ToLowerInvariant(),
ClassStyle =
settings.ImmutableRecords ||
settings.CodeGeneratorSettings?.GenerateNativeRecords is true
? CSharpClassStyle.Record
: CSharpClassStyle.Poco,
GenerateNativeRecords =
settings.ImmutableRecords ||
settings.CodeGeneratorSettings?.GenerateNativeRecords is true,
}
};

if (settings.UseSystemTextJsonPolymorphicSerialization)
{
csharpClientGeneratorSettings.CSharpGeneratorSettings.TemplateFactory = new CustomTemplateFactory(
csharpClientGeneratorSettings.CSharpGeneratorSettings,
[
typeof(CSharpGenerator).Assembly,
typeof(CSharpGeneratorBaseSettings).Assembly,
typeof(CustomTemplateFactory).Assembly,
]);
}

var generator = new CustomCSharpClientGenerator(
document,
new CSharpClientGeneratorSettings
{
GenerateClientClasses = false,
GenerateDtoTypes = true,
GenerateClientInterfaces = false,
GenerateExceptionClasses = false,
CodeGeneratorSettings = { PropertyNameGenerator = new CustomCSharpPropertyNameGenerator(), },
CSharpGeneratorSettings =
{
Namespace = settings.ContractsNamespace ?? settings.Namespace,
JsonLibrary = CSharpJsonLibrary.SystemTextJson,
TypeAccessModifier = settings.TypeAccessibility.ToString().ToLowerInvariant(),
ClassStyle =
settings.ImmutableRecords ||
settings.CodeGeneratorSettings?.GenerateNativeRecords is true
? CSharpClassStyle.Record
: CSharpClassStyle.Poco,
GenerateNativeRecords =
settings.ImmutableRecords ||
settings.CodeGeneratorSettings?.GenerateNativeRecords is true,
}
});
csharpClientGeneratorSettings);

MapCSharpGeneratorSettings(
settings.CodeGeneratorSettings,
Expand Down Expand Up @@ -82,4 +97,42 @@ private static void MapCSharpGeneratorSettings(
settingsProperty.SetValue(destination, value);
}
}
}

/// <summary>
/// custom template factory
/// solely for the purpose of supporting UseSystemTextJsonPolymorphicSerialization
/// This class and its templates should be removed when NSwag supports this feature.
/// </summary>
private class CustomTemplateFactory : NSwag.CodeGeneration.DefaultTemplateFactory
{
/// <summary>Initializes a new instance of the <see cref="DefaultTemplateFactory" /> class.</summary>
/// <param name="settings">The settings.</param>
/// <param name="assemblies">The assemblies.</param>
public CustomTemplateFactory(CodeGeneratorSettingsBase settings, Assembly[] assemblies)
: base(settings, assemblies)
{
}

/// <summary>Tries to load an embedded Liquid template.</summary>
/// <param name="language">The language.</param>
/// <param name="template">The template name.</param>
/// <returns>The template.</returns>
protected override string GetEmbeddedLiquidTemplate(string language, string template)
{
template = template.TrimEnd('!');
var assembly = GetLiquidAssembly("Refitter.Core");
var resourceName = $"Refitter.Core.Templates.{template}.liquid";

var resource = assembly.GetManifestResourceStream(resourceName);
if (resource != null)
{
using (var reader = new StreamReader(resource))
{
return reader.ReadToEnd();
}
}

return base.GetEmbeddedLiquidTemplate(language, template);
}
}
}
4 changes: 4 additions & 0 deletions src/Refitter.Core/Refitter.Core.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@
</AssemblyAttribute>
</ItemGroup>

<ItemGroup>
<EmbeddedResource Include="Templates\*.liquid" />
</ItemGroup>

</Project>
10 changes: 8 additions & 2 deletions src/Refitter.Core/Settings/RefitGeneratorSettings.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Text.Json.Serialization;

namespace Refitter.Core;
Expand Down Expand Up @@ -216,4 +216,10 @@ public class RefitGeneratorSettings
/// Dependency Injection is written to a file called DependencyInjection.cs
/// </summary>
public bool GenerateMultipleFiles { get; set; }
}

/// <summary>
/// Set to <c>true</c> to use System.Text.Json polymorphic serialization. Default is <c>false</c>
/// Gets a value indicating whether to use System.Text.Json polymorphic serialization
/// </summary>
public bool UseSystemTextJsonPolymorphicSerialization { get; set; }
}
148 changes: 148 additions & 0 deletions src/Refitter.Core/Templates/Class.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
{%- if HasDescription -%}
/// <summary>
/// {{ Description | csharpdocs }}
/// </summary>
{%- endif -%}
{%- if HasDiscriminator -%}
{%- if UseSystemTextJson -%}
[System.Text.Json.Serialization.JsonPolymorphic(TypeDiscriminatorPropertyName = "{{ Discriminator }}", UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
{%- else -%}
[Newtonsoft.Json.JsonConverter(typeof(JsonInheritanceConverter), "{{ Discriminator }}")]
{%- endif -%}
{%- for derivedClass in DerivedClasses -%}
{%- if derivedClass.IsAbstract != true -%}
{%- if UseSystemTextJson -%}
[System.Text.Json.Serialization.JsonDerivedType(typeof({{ derivedClass.ClassName }}), typeDiscriminator: "{{ derivedClass.Discriminator }}")]
{%- endif -%}
{%- endif -%}
{%- endfor -%}
{%- endif -%}
[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "{{ ToolchainVersion }}")]
{%- if InheritsExceptionSchema -%}
{%- if UseSystemTextJson -%}
// TODO(system.text.json): What to do here?
{%- else -%}
[Newtonsoft.Json.JsonObjectAttribute]
{%- endif -%}
{%- endif -%}
{%- if IsDeprecated -%}
[System.Obsolete{% if HasDeprecatedMessage %}({{ DeprecatedMessage | literal }}){% endif %}]
{% endif -%}
{%- template Class.Annotations -%}
{{ TypeAccessModifier }} {% if IsAbstract %}abstract {% endif %}partial {% if GenerateNativeRecords %}record{% else %}class{% endif %} {{ClassName}} {%- template Class.Inheritance %}
{
{%- if IsTuple -%}
public {{ ClassName }}({%- for tupleType in TupleTypes %}{{ tupleType }} item{{ forloop.index }}{%- if forloop.last == false %}, {% endif %}{% endfor %}) : base({%- for tupleType in TupleTypes %}item{{ forloop.index }}{%- if forloop.last == false %}, {% endif %}{% endfor %})
{
}

{%- endif -%}
{%- if RenderInpc or RenderPrism -%}
{%- for property in Properties -%}
private {{ property.Type }} {{ property.FieldName }}{%- if property.HasDefaultValue %} = {{ property.DefaultValue }}{% elsif GenerateNullableReferenceTypes %} = default!{%- endif %};
{%- endfor -%}

{%- endif -%}
{%- template Class.Constructor -%}
{%- if RenderRecord -%}
{% template Class.Constructor.Record -%}
{%- endif -%}
{%- for property in Properties -%}
{%- if property.HasDescription -%}
/// <summary>
/// {{ property.Description | csharpdocs }}
/// </summary>
{%- endif -%}
{%- if UseSystemTextJson %}
[System.Text.Json.Serialization.JsonPropertyName("{{ property.Name }}")]
{%- if property.IsStringEnumArray %}
// TODO(system.text.json): Add string enum item converter
{%- endif -%}
{%- else -%}
[Newtonsoft.Json.JsonProperty("{{ property.Name }}", Required = {{ property.JsonPropertyRequiredCode }}{% if property.IsStringEnumArray %}, ItemConverterType = typeof(Newtonsoft.Json.Converters.StringEnumConverter){% endif %})]
{%- endif -%}
{%- if property.RenderRequiredAttribute -%}
[System.ComponentModel.DataAnnotations.Required{% if property.AllowEmptyStrings %}(AllowEmptyStrings = true){% endif %}]
{%- endif -%}
{%- if property.RenderRangeAttribute -%}
[System.ComponentModel.DataAnnotations.Range({{ property.RangeMinimumValue }}, {{ property.RangeMaximumValue }})]
{%- endif -%}
{%- if property.RenderStringLengthAttribute -%}
[System.ComponentModel.DataAnnotations.StringLength({{ property.StringLengthMaximumValue }}{% if property.StringLengthMinimumValue > 0 %}, MinimumLength = {{ property.StringLengthMinimumValue }}{% endif %})]
{%- endif -%}
{%- if property.RenderMinLengthAttribute -%}
[System.ComponentModel.DataAnnotations.MinLength({{ property.MinLengthAttribute }})]
{%- endif -%}
{%- if property.RenderMaxLengthAttribute -%}
[System.ComponentModel.DataAnnotations.MaxLength({{ property.MaxLengthAttribute }})]
{%- endif -%}
{%- if property.RenderRegularExpressionAttribute -%}
[System.ComponentModel.DataAnnotations.RegularExpression(@"{{ property.RegularExpressionValue }}")]
{%- endif -%}
{%- if property.IsStringEnum -%}
{%- if UseSystemTextJson -%}
[System.Text.Json.Serialization.JsonConverter(typeof(System.Text.Json.Serialization.JsonStringEnumConverter))]
{%- else -%}
[Newtonsoft.Json.JsonConverter(typeof(Newtonsoft.Json.Converters.StringEnumConverter))]
{%- endif -%}
{%- endif -%}
{%- if property.IsDate and UseDateFormatConverter -%}
{%- if UseSystemTextJson -%}
[System.Text.Json.Serialization.JsonConverter(typeof(DateFormatConverter))]
{%- else -%}
[Newtonsoft.Json.JsonConverter(typeof(DateFormatConverter))]
{%- endif -%}
{%- endif -%}
{%- if property.IsDeprecated -%}
[System.Obsolete{% if property.HasDeprecatedMessage %}({{ property.DeprecatedMessage | literal }}){% endif %}]
{%- endif -%}
{%- template Class.Property.Annotations -%}
public {{ property.Type }} {{ property.PropertyName }}{% if RenderInpc == false and RenderPrism == false %} { get; {% if property.HasSetter and RenderRecord == false %}set; {% elsif RenderRecord and GenerateNativeRecords %}init; {% endif %}}{% if property.HasDefaultValue and RenderRecord == false %} = {{ property.DefaultValue }};{% elsif GenerateNullableReferenceTypes and RenderRecord == false %} = default!;{% endif %}
{% else %}
{
get { return {{ property.FieldName }}; }

{%- if property.HasSetter -%}
{%- if RenderInpc -%}
{{PropertySetterAccessModifier}}set
{
if ({{ property.FieldName }} != value)
{
{{ property.FieldName }} = value;
RaisePropertyChanged();
}
}
{%- else -%}
{{PropertySetterAccessModifier}}set { SetProperty(ref {{ property.FieldName }}, value); }
{%- endif -%}
{%- endif -%}
}
{%- endif %}
{%- endfor -%}

{%- if GenerateAdditionalPropertiesProperty -%}

private System.Collections.Generic.IDictionary<string, {{ AdditionalPropertiesType }}>{% if GenerateNullableReferenceTypes %}?{% endif %} _additionalProperties;

{%- if UseSystemTextJson -%}
[System.Text.Json.Serialization.JsonExtensionData]
{%- else -%}
[Newtonsoft.Json.JsonExtensionData]
{%- endif -%}
public System.Collections.Generic.IDictionary<string, {{ AdditionalPropertiesType }}> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new System.Collections.Generic.Dictionary<string, {{ AdditionalPropertiesType }}>()); }
{{PropertySetterAccessModifier}}set { _additionalProperties = value; }
}

{%- endif -%}
{%- if GenerateJsonMethods -%}
{% template Class.ToJson %}
{% template Class.FromJson %}

{%- endif -%}
{%- if RenderInpc -%}
{% template Class.Inpc %}
{%- endif -%}
{% template Class.Body %}
}
Empty file.
Empty file.
Loading
Loading