From 6bb9f114b4318e1302d0f58f2861f2e156bfa3b0 Mon Sep 17 00:00:00 2001 From: martincostello Date: Sat, 16 Aug 2025 13:18:59 +0100 Subject: [PATCH 1/4] [Validation] Use [JsonPropertName] for display Use `[JsonPropertyName("{value}")]` for the display name, if `[Display(Name = "{value}")` is not specified, instead of the property's name. Contributes to #63290. --- src/Shared/RoslynUtils/WellKnownTypeData.cs | 2 ++ .../gen/Extensions/ISymbolExtensions.cs | 20 +++++++++++--- .../ValidationsGenerator.TypesParser.cs | 19 ++++++++++--- .../ValidationsGenerator.ComplexType.cs | 16 +++++++++++ ...nore#ValidatableInfoResolver.g.verified.cs | 27 +++++++++++++++++++ 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/src/Shared/RoslynUtils/WellKnownTypeData.cs b/src/Shared/RoslynUtils/WellKnownTypeData.cs index 4311eed4c6ac..38bc35ac8f90 100644 --- a/src/Shared/RoslynUtils/WellKnownTypeData.cs +++ b/src/Shared/RoslynUtils/WellKnownTypeData.cs @@ -120,6 +120,7 @@ public enum WellKnownType System_AttributeUsageAttribute, System_Text_Json_Serialization_JsonDerivedTypeAttribute, System_Text_Json_Serialization_JsonIgnoreAttribute, + System_Text_Json_Serialization_JsonPropertyNameAttribute, System_ComponentModel_DataAnnotations_DisplayAttribute, System_ComponentModel_DataAnnotations_ValidationAttribute, System_ComponentModel_DataAnnotations_RequiredAttribute, @@ -243,6 +244,7 @@ public enum WellKnownType "System.AttributeUsageAttribute", "System.Text.Json.Serialization.JsonDerivedTypeAttribute", "System.Text.Json.Serialization.JsonIgnoreAttribute", + "System.Text.Json.Serialization.JsonPropertyNameAttribute", "System.ComponentModel.DataAnnotations.DisplayAttribute", "System.ComponentModel.DataAnnotations.ValidationAttribute", "System.ComponentModel.DataAnnotations.RequiredAttribute", diff --git a/src/Validation/gen/Extensions/ISymbolExtensions.cs b/src/Validation/gen/Extensions/ISymbolExtensions.cs index a770b7260656..d84e1494d8be 100644 --- a/src/Validation/gen/Extensions/ISymbolExtensions.cs +++ b/src/Validation/gen/Extensions/ISymbolExtensions.cs @@ -10,7 +10,7 @@ namespace Microsoft.Extensions.Validation; internal static class ISymbolExtensions { - public static string GetDisplayName(this ISymbol property, INamedTypeSymbol displayAttribute) + public static string? GetDisplayName(this ISymbol property, INamedTypeSymbol displayAttribute) { var displayNameAttribute = property.GetAttributes() .FirstOrDefault(attribute => @@ -19,19 +19,33 @@ attribute.AttributeClass is { } attributeClass && if (displayNameAttribute is not null) { + // For the [Foo(Name = "bar")] case if (!displayNameAttribute.NamedArguments.IsDefaultOrEmpty) { foreach (var namedArgument in displayNameAttribute.NamedArguments) { if (string.Equals(namedArgument.Key, "Name", StringComparison.Ordinal)) { - return namedArgument.Value.Value?.ToString() ?? property.Name; + if (namedArgument.Value.Value?.ToString() is { } name) + { + return name; + } } } } + + // For the [Foo("bar")] case + if (displayNameAttribute.ConstructorArguments.Length is 1) + { + var arg = displayNameAttribute.ConstructorArguments[0]; + if (arg.Kind == TypedConstantKind.Primitive && arg.Value is string name) + { + return name; + } + } } - return property.Name; + return null; } public static bool IsEqualityContract(this IPropertySymbol prop, WellKnownTypes wellKnownTypes) => diff --git a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs index 7544c9d72feb..0042e2e0f8f7 100644 --- a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs +++ b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs @@ -5,7 +5,6 @@ using System.Collections.Immutable; using System.Linq; using Microsoft.AspNetCore.Analyzers.Infrastructure; -using Microsoft.AspNetCore.Analyzers.RouteEmbeddedLanguage.Infrastructure; using Microsoft.AspNetCore.App.Analyzers.Infrastructure; using Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel; using Microsoft.CodeAnalysis; @@ -195,12 +194,19 @@ internal ImmutableArray ExtractValidatableMembers(ITypeSymb ref validatableTypes, ref visitedTypes); + var displayName = + parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? + parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? + correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? + correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? + parameter.Name ?? + correspondingProperty.Name; + members.Add(new ValidatableProperty( ContainingType: correspondingProperty.ContainingType, Type: correspondingProperty.Type, Name: correspondingProperty.Name, - DisplayName: parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? - correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)), + DisplayName: displayName, Attributes: [])); } } @@ -252,11 +258,16 @@ internal ImmutableArray ExtractValidatableMembers(ITypeSymb continue; } + var displayName = + member.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? + member.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? + member.Name; + members.Add(new ValidatableProperty( ContainingType: member.ContainingType, Type: member.Type, Name: member.Name, - DisplayName: member.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)), + DisplayName: displayName, Attributes: attributes)); } diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ComplexType.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ComplexType.cs index d4bd0ef579f5..9ef24191a4c7 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ComplexType.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ComplexType.cs @@ -32,6 +32,7 @@ public async Task CanValidateComplexTypesWithJsonIgnore() app.MapPost("/complex-type-with-json-ignore", (ComplexTypeWithJsonIgnore complexType) => Results.Ok("Passed"!)); app.MapPost("/record-type-with-json-ignore", (RecordTypeWithJsonIgnore recordType) => Results.Ok("Passed"!)); +app.MapPost("/complex-type-with-json-property-name", (ComplexTypeWithJsonPropertyName complexType) => Results.Ok("Passed"!)); app.Run(); @@ -76,6 +77,21 @@ public record CircularReferenceRecord public string Name { get; set; } = "test"; } + +public class ComplexTypeWithJsonPropertyName +{ + [Range(10, 100)] + public int DefaultPropertyName { get; set; } = 10; + + [Range(10, 100)] + [JsonPropertyName("custom-property-name")] + public int CustomJsonPropertyName { get; set; } = 20; + + [Display(Name = "display-name")] + [Range(10, 100)] + [JsonPropertyName("custom-property-name-with-display-name")] + public int CustomJsonPropertyNameWithDisplayName { get; set; } = 30; +} """; await Verify(source, out var compilation); await VerifyEndpoint(compilation, "/complex-type-with-json-ignore", async (endpoint, serviceProvider) => diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypesWithJsonIgnore#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypesWithJsonIgnore#ValidatableInfoResolver.g.verified.cs index c6957d8a67ec..1e5cd616ba4c 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypesWithJsonIgnore#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateComplexTypesWithJsonIgnore#ValidatableInfoResolver.g.verified.cs @@ -101,6 +101,33 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. ); return true; } + if (type == typeof(global::ComplexTypeWithJsonPropertyName)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ComplexTypeWithJsonPropertyName), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithJsonPropertyName), + propertyType: typeof(int), + name: "DefaultPropertyName", + displayName: "DefaultPropertyName" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithJsonPropertyName), + propertyType: typeof(int), + name: "CustomJsonPropertyName", + displayName: "custom-property-name" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ComplexTypeWithJsonPropertyName), + propertyType: typeof(int), + name: "CustomJsonPropertyNameWithDisplayName", + displayName: "display-name" + ), + ] + ); + return true; + } return false; } From 8844f5ee35dcd4e4ae48a15f5b7a0be5453ec772 Mon Sep 17 00:00:00 2001 From: martincostello Date: Mon, 18 Aug 2025 14:24:51 +0100 Subject: [PATCH 2/4] Add own method Add own method for getting `[JsonPropertyName]` instead of mingling with `[Display]`. --- .../gen/Extensions/ISymbolExtensions.cs | 15 +++++++++++++-- .../Parsers/ValidationsGenerator.TypesParser.cs | 6 +++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/Validation/gen/Extensions/ISymbolExtensions.cs b/src/Validation/gen/Extensions/ISymbolExtensions.cs index d84e1494d8be..fdf2a48940b9 100644 --- a/src/Validation/gen/Extensions/ISymbolExtensions.cs +++ b/src/Validation/gen/Extensions/ISymbolExtensions.cs @@ -19,7 +19,6 @@ attribute.AttributeClass is { } attributeClass && if (displayNameAttribute is not null) { - // For the [Foo(Name = "bar")] case if (!displayNameAttribute.NamedArguments.IsDefaultOrEmpty) { foreach (var namedArgument in displayNameAttribute.NamedArguments) @@ -33,8 +32,20 @@ attribute.AttributeClass is { } attributeClass && } } } + } + + return null; + } - // For the [Foo("bar")] case + public static string? GetJsonPropertyName(this ISymbol property, INamedTypeSymbol jsonPropertyNameAttribute) + { + var displayNameAttribute = property.GetAttributes() + .FirstOrDefault(attribute => + attribute.AttributeClass is { } attributeClass && + SymbolEqualityComparer.Default.Equals(attributeClass, jsonPropertyNameAttribute)); + + if (displayNameAttribute is not null) + { if (displayNameAttribute.ConstructorArguments.Length is 1) { var arg = displayNameAttribute.ConstructorArguments[0]; diff --git a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs index 0042e2e0f8f7..ae7c5371dd7b 100644 --- a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs +++ b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs @@ -196,9 +196,9 @@ internal ImmutableArray ExtractValidatableMembers(ITypeSymb var displayName = parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? - parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? + parameter.GetJsonPropertyName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? - correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? + correspondingProperty.GetJsonPropertyName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? parameter.Name ?? correspondingProperty.Name; @@ -260,7 +260,7 @@ internal ImmutableArray ExtractValidatableMembers(ITypeSymb var displayName = member.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? - member.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? + member.GetJsonPropertyName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? member.Name; members.Add(new ValidatableProperty( From 35dde071876a5a065dc3f04926390ef6269a8b95 Mon Sep 17 00:00:00 2001 From: martincostello Date: Mon, 18 Aug 2025 14:27:22 +0100 Subject: [PATCH 3/4] Rename parameters Give better names. --- src/Validation/gen/Extensions/ISymbolExtensions.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Validation/gen/Extensions/ISymbolExtensions.cs b/src/Validation/gen/Extensions/ISymbolExtensions.cs index fdf2a48940b9..798536766e6f 100644 --- a/src/Validation/gen/Extensions/ISymbolExtensions.cs +++ b/src/Validation/gen/Extensions/ISymbolExtensions.cs @@ -37,18 +37,18 @@ attribute.AttributeClass is { } attributeClass && return null; } - public static string? GetJsonPropertyName(this ISymbol property, INamedTypeSymbol jsonPropertyNameAttribute) + public static string? GetJsonPropertyName(this ISymbol property, INamedTypeSymbol nameAttribute) { - var displayNameAttribute = property.GetAttributes() + var jsonPropertyNameAttribute = property.GetAttributes() .FirstOrDefault(attribute => attribute.AttributeClass is { } attributeClass && - SymbolEqualityComparer.Default.Equals(attributeClass, jsonPropertyNameAttribute)); + SymbolEqualityComparer.Default.Equals(attributeClass, nameAttribute)); - if (displayNameAttribute is not null) + if (jsonPropertyNameAttribute is not null) { - if (displayNameAttribute.ConstructorArguments.Length is 1) + if (jsonPropertyNameAttribute.ConstructorArguments.Length is 1) { - var arg = displayNameAttribute.ConstructorArguments[0]; + var arg = jsonPropertyNameAttribute.ConstructorArguments[0]; if (arg.Kind == TypedConstantKind.Primitive && arg.Value is string name) { return name; From 51614a09b72b358a547256fb7a77b63e9c1f0396 Mon Sep 17 00:00:00 2001 From: martincostello Date: Mon, 18 Aug 2025 14:44:27 +0100 Subject: [PATCH 4/4] Fix ordering - Check for `[Display]` before any `[JsonPropertyName]`. - Add test case for records. --- .../ValidationsGenerator.TypesParser.cs | 2 +- .../ValidationsGenerator.ComplexType.cs | 2 +- .../ValidationsGenerator.RecordType.cs | 17 +++++++++- ...ypes#ValidatableInfoResolver.g.verified.cs | 33 +++++++++++++++++++ 4 files changed, 51 insertions(+), 3 deletions(-) diff --git a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs index ae7c5371dd7b..e489da6d72e6 100644 --- a/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs +++ b/src/Validation/gen/Parsers/ValidationsGenerator.TypesParser.cs @@ -196,8 +196,8 @@ internal ImmutableArray ExtractValidatableMembers(ITypeSymb var displayName = parameter.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? - parameter.GetJsonPropertyName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? correspondingProperty.GetDisplayName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_ComponentModel_DataAnnotations_DisplayAttribute)) ?? + parameter.GetJsonPropertyName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? correspondingProperty.GetJsonPropertyName(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Text_Json_Serialization_JsonPropertyNameAttribute)) ?? parameter.Name ?? correspondingProperty.Name; diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ComplexType.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ComplexType.cs index 9ef24191a4c7..c7ff824dc142 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ComplexType.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.ComplexType.cs @@ -15,6 +15,7 @@ public async Task CanValidateComplexTypesWithJsonIgnore() using System; using System.ComponentModel.DataAnnotations; using System.Collections.Generic; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -22,7 +23,6 @@ public async Task CanValidateComplexTypesWithJsonIgnore() using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.AspNetCore.Mvc; -using System.Text.Json.Serialization; var builder = WebApplication.CreateBuilder(); diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.RecordType.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.RecordType.cs index a5a4b2e09ed0..af223cf495bb 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.RecordType.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/ValidationsGenerator.RecordType.cs @@ -15,6 +15,7 @@ public async Task CanValidateRecordTypes() using System; using System.ComponentModel.DataAnnotations; using System.Collections.Generic; +using System.Text.Json.Serialization; using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; @@ -31,6 +32,7 @@ public async Task CanValidateRecordTypes() var app = builder.Build(); app.MapPost("/validatable-record", (ValidatableRecord validatableRecord) => Results.Ok("Passed"!)); +app.MapPost("/validatable-record-with-json-property-name", (ValidatableRecordWithJsonPropertyNames validatableRecord) => Results.Ok("Passed"!)); app.Run(); @@ -95,7 +97,20 @@ public record ValidatableRecord( [DerivedValidation, Range(10, 100)] int PropertyWithMultipleAttributes = 10, [FromServices] [Required] TestService ServiceProperty = null!, // This should be ignored because of [FromServices] - [FromKeyedServices("serviceKey")] [Range(10, 100)] int KeyedServiceProperty = 5 // This should be ignored because of [FromKeyedServices] + [FromKeyedServices("serviceKey")] [Range(10, 100)] int KeyedServiceProperty = 5, // This should be ignored because of [FromKeyedServices] + [Display(Name = "display-name")] [Range(10, 100)] int IntegerWithRangeAndDisplay = 10 +); + +public record ValidatableRecordWithJsonPropertyNames( + [Range(10, 100)] + int DefaultPropertyName = 10, + [Range(10, 100)] + [property: JsonPropertyName("custom-property-name")] + int CustomJsonPropertyName = 20, + [Display(Name = "display-name")] + [Range(10, 100)] + [property: JsonPropertyName("custom-property-name-with-display-name")] + int CustomJsonPropertyNameWithDisplayName = 30 ); """; await Verify(source, out var compilation); diff --git a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs index e308777967aa..8bc91d6fc814 100644 --- a/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs +++ b/src/Validation/test/Microsoft.Extensions.Validation.GeneratorTests/snapshots/ValidationsGeneratorTests.CanValidateRecordTypes#ValidatableInfoResolver.g.verified.cs @@ -193,6 +193,39 @@ public bool TryGetValidatableTypeInfo(global::System.Type type, [global::System. name: "PropertyWithMultipleAttributes", displayName: "PropertyWithMultipleAttributes" ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecord), + propertyType: typeof(int), + name: "IntegerWithRangeAndDisplay", + displayName: "display-name" + ), + ] + ); + return true; + } + if (type == typeof(global::ValidatableRecordWithJsonPropertyNames)) + { + validatableInfo = new GeneratedValidatableTypeInfo( + type: typeof(global::ValidatableRecordWithJsonPropertyNames), + members: [ + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecordWithJsonPropertyNames), + propertyType: typeof(int), + name: "DefaultPropertyName", + displayName: "DefaultPropertyName" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecordWithJsonPropertyNames), + propertyType: typeof(int), + name: "CustomJsonPropertyName", + displayName: "custom-property-name" + ), + new GeneratedValidatablePropertyInfo( + containingType: typeof(global::ValidatableRecordWithJsonPropertyNames), + propertyType: typeof(int), + name: "CustomJsonPropertyNameWithDisplayName", + displayName: "display-name" + ), ] ); return true;