diff --git a/Directory.Build.props b/Directory.Build.props index aca9fbb92..ccc140a26 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,31 +1,44 @@ - - 11.0.0 - - Rico Suter - Copyright © Rico Suter, 2022 - - True - ../NJsonSchema.snk - - json schema validation generator .net - http://NJsonSchema.org - JSON Schema reader, generator and validator for .NET - NuGetIcon.png - MIT - - true - true - true - snupkg - True - - latest - true - enable - - true - - + + 11.0.0 + + Rico Suter + Copyright © Rico Suter, 2022 + + True + ../NJsonSchema.snk + + json schema validation generator .net + http://NJsonSchema.org + JSON Schema reader, generator and validator for .NET + NuGetIcon.png + MIT + + true + true + true + snupkg + True + + latest + true + enable + + true + + + + + true + latest-Recommended + true + + $(NoWarn);CA1200;CA1510;CA1716;CA1720 + diff --git a/Directory.Packages.props b/Directory.Packages.props index c7ac7a7fd..94d8c431d 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,10 +5,10 @@ - + - + @@ -18,13 +18,13 @@ - - - + + + - - + + \ No newline at end of file diff --git a/src/NJsonSchema.Annotations/IJsonSchemaExtensionDataAttribute.cs b/src/NJsonSchema.Annotations/IJsonSchemaExtensionDataAttribute.cs index d440dfc44..f6c42e608 100644 --- a/src/NJsonSchema.Annotations/IJsonSchemaExtensionDataAttribute.cs +++ b/src/NJsonSchema.Annotations/IJsonSchemaExtensionDataAttribute.cs @@ -11,7 +11,9 @@ namespace NJsonSchema.Annotations; /// Interface to add an extension data property to a class or property, implementation needs to inherit from System.Attribute. +#pragma warning disable CA1711 // Rename type name IJsonSchemaExtensionDataAttribute so that it does not end in 'Attribute' public interface IJsonSchemaExtensionDataAttribute +#pragma warning restore CA1711 { /// Gets the extension data. IReadOnlyDictionary ExtensionData { get; } diff --git a/src/NJsonSchema.Benchmark/NJsonSchema.Benchmark.csproj b/src/NJsonSchema.Benchmark/NJsonSchema.Benchmark.csproj index 1f7792c19..f889f9404 100644 --- a/src/NJsonSchema.Benchmark/NJsonSchema.Benchmark.csproj +++ b/src/NJsonSchema.Benchmark/NJsonSchema.Benchmark.csproj @@ -6,6 +6,7 @@ $(NoWarn),xUnit1013 false disable + false diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/NJsonSchema.CodeGeneration.CSharp.Tests.csproj b/src/NJsonSchema.CodeGeneration.CSharp.Tests/NJsonSchema.CodeGeneration.CSharp.Tests.csproj index 408d7f362..d5ea2de05 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/NJsonSchema.CodeGeneration.CSharp.Tests.csproj +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/NJsonSchema.CodeGeneration.CSharp.Tests.csproj @@ -6,6 +6,7 @@ true $(NoWarn),1587,1998,1591,618,SYSLIB0012 disable + false diff --git a/src/NJsonSchema.CodeGeneration.CSharp.Tests/NullableReferenceTypesTests.cs b/src/NJsonSchema.CodeGeneration.CSharp.Tests/NullableReferenceTypesTests.cs index a0c98bf78..f33422088 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp.Tests/NullableReferenceTypesTests.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp.Tests/NullableReferenceTypesTests.cs @@ -245,5 +245,48 @@ public async Task When_generating_from_json_schema_string_property_with_date_or_ Assert.Contains("public string Required { get; set; } = default!;", code); Assert.Contains("public string? Optional { get; set; } = default!;", code); } + + [Fact] + public async Task + When_generating_from_json_schema_string_property_with_reference_is_optional_and_GenerateNullableOptionalProperties_is_set_then_CSharp_property() + { + //// Arrange + var schemaJson = @" + { + ""type"": ""object"", + ""required"": [ + ""required"" + ], + ""properties"": { + ""required"": { ""$ref"": ""#/$defs/requiredString"" }, + ""optional"": { ""$ref"": ""#/$defs/optionalString"" } + }, + ""$defs"": { + ""requiredString"": { ""type"": ""string"" }, + ""optionalString"": { ""type"": ""string"" } + } + } + "; + + var schema = await JsonSchema.FromJsonAsync(schemaJson); + + //// Act + var generator = new CSharpGenerator(schema, new CSharpGeneratorSettings + { + ClassStyle = CSharpClassStyle.Poco, + SchemaType = SchemaType.OpenApi3, + DateType = "string", + DateTimeType = "string", + TimeType = "string", + TimeSpanType = "string", + GenerateNullableReferenceTypes = true, + GenerateOptionalPropertiesAsNullable = true + }); + var code = generator.GenerateFile("MyClass"); + + //// Assert + Assert.Contains("public string Required { get; set; } = default!;", code); + Assert.Contains("public string? Optional { get; set; } = default!;", code); + } } } \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGenerator.cs index 285e6a221..de2fd2574 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpGenerator.cs @@ -81,13 +81,13 @@ public override IEnumerable GenerateTypes() } /// - protected override string GenerateFile(IEnumerable artifactCollection) + protected override string GenerateFile(IEnumerable artifacts) { var model = new FileTemplateModel { Namespace = Settings.Namespace ?? string.Empty, GenerateNullableReferenceTypes = Settings.GenerateNullableReferenceTypes, - TypesCode = artifactCollection.Concatenate() + TypesCode = artifacts.Concatenate() }; var template = Settings.TemplateFactory.CreateTemplate("CSharp", "File", model); diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpJsonSerializerGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpJsonSerializerGenerator.cs index 1f13fd59e..815d8dd68 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpJsonSerializerGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpJsonSerializerGenerator.cs @@ -22,7 +22,7 @@ public static class CSharpJsonSerializerGenerator public static string GenerateJsonSerializerParameterCode(CSharpGeneratorSettings settings, IEnumerable? additionalJsonConverters) { var jsonConverters = GetJsonConverters(settings, additionalJsonConverters); - var hasJsonConverters = jsonConverters.Any(); + var hasJsonConverters = jsonConverters.Count > 0; return GenerateForJsonLibrary(settings, jsonConverters, hasJsonConverters); } @@ -95,24 +95,17 @@ private static string GenerateForJsonLibrary(CSharpGeneratorSettings settings, L private static string GenerateConverters(List jsonConverters, CSharpJsonLibrary jsonLibrary) { - if (jsonConverters.Any()) + if (jsonConverters.Count > 0) { - switch (jsonLibrary) + return jsonLibrary switch { - case CSharpJsonLibrary.NewtonsoftJson: - return "new Newtonsoft.Json.JsonConverter[] { " + string.Join(", ", jsonConverters.Select(c => "new " + c + "()")) + " }"; - - case CSharpJsonLibrary.SystemTextJson: - return "new System.Text.Json.Serialization.JsonConverter[] { " + string.Join(", ", jsonConverters.Select(c => "new " + c + "()")) + " }"; - - default: // TODO: possibly add more json converters - return string.Empty; - } - } - else - { - return string.Empty; + CSharpJsonLibrary.NewtonsoftJson => "new Newtonsoft.Json.JsonConverter[] { " + string.Join(", ", jsonConverters.Select(c => "new " + c + "()")) + " }", + CSharpJsonLibrary.SystemTextJson => "new System.Text.Json.Serialization.JsonConverter[] { " + string.Join(", ", jsonConverters.Select(c => "new " + c + "()")) + " }", + _ => string.Empty + }; } + + return string.Empty; } } } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs index 2c61c752f..2a42c6df8 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpPropertyNameGenerator.cs @@ -11,7 +11,7 @@ namespace NJsonSchema.CodeGeneration.CSharp /// Generates the property name for a given CSharp . public sealed class CSharpPropertyNameGenerator : IPropertyNameGenerator { - private static readonly char[] _reservedFirstPassChars = { '"', '\'', '@', '?', '!', '$', '[', ']', '(', ')', '.', '=', '+' }; + private static readonly char[] _reservedFirstPassChars = { '"', '\'', '@', '?', '!', '$', '[', ']', '(', ')', '.', '=', '+', '|' }; private static readonly char[] _reservedSecondPassChars = { '*', ':', '-', '#', '&' }; /// Generates the property name. @@ -35,7 +35,8 @@ public string Generate(JsonSchemaProperty property) .Replace(")", string.Empty) .Replace(".", "-") .Replace("=", "-") - .Replace("+", "plus"); + .Replace("+", "plus") + .Replace("|", "_"); } name = ConversionUtilities.ConvertToUpperCamelCase(name, true); @@ -53,4 +54,4 @@ public string Generate(JsonSchemaProperty property) return name; } } -} \ No newline at end of file +} diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs index 13ddb5ef5..5f329158e 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpTypeResolver.cs @@ -60,13 +60,6 @@ public string Resolve(JsonSchema schema, bool isNullable, string? typeNameHint, throw new ArgumentNullException(nameof(schema)); } - schema = GetResolvableSchema(schema); - - if (schema == ExceptionSchema) - { - return "System.Exception"; - } - // Primitive schemas (no new type) if (Settings.GenerateOptionalPropertiesAsNullable && schema is JsonSchemaProperty property && @@ -75,6 +68,13 @@ schema is JsonSchemaProperty property && isNullable = true; } + schema = GetResolvableSchema(schema); + + if (schema == ExceptionSchema) + { + return "System.Exception"; + } + var markAsNullableType = Settings.GenerateNullableReferenceTypes && isNullable; if (schema.ActualTypeSchema.IsAnyType && @@ -217,7 +217,7 @@ private static string ResolveBoolean(bool isNullable) return isNullable ? "bool?" : "bool"; } - private string ResolveInteger(JsonSchema schema, bool isNullable, string? typeNameHint) + private static string ResolveInteger(JsonSchema schema, bool isNullable, string? typeNameHint) { if (schema.Format == JsonFormatStrings.Byte) { diff --git a/src/NJsonSchema.CodeGeneration.CSharp/CSharpValueGenerator.cs b/src/NJsonSchema.CodeGeneration.CSharp/CSharpValueGenerator.cs index 851724d8d..07356edf9 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/CSharpValueGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/CSharpValueGenerator.cs @@ -67,11 +67,11 @@ public CSharpValueGenerator(CSharpGeneratorSettings settings) if (schema.Type.IsArray() || schema.Type.IsObject()) { - targetType = !string.IsNullOrEmpty(_settings.DictionaryInstanceType) && targetType.StartsWith(_settings.DictionaryType + "<") + targetType = !string.IsNullOrEmpty(_settings.DictionaryInstanceType) && targetType.StartsWith(_settings.DictionaryType + "<", StringComparison.Ordinal) ? _settings.DictionaryInstanceType + targetType.Substring(_settings.DictionaryType.Length) : targetType; - targetType = !string.IsNullOrEmpty(_settings.ArrayInstanceType) && targetType.StartsWith(_settings.ArrayType + "<") + targetType = !string.IsNullOrEmpty(_settings.ArrayInstanceType) && targetType.StartsWith(_settings.ArrayType + "<", StringComparison.Ordinal) ? _settings.ArrayInstanceType + targetType.Substring(_settings.ArrayType.Length) : targetType; @@ -93,11 +93,11 @@ public override string GetNumericValue(JsonObjectType type, object value, string switch (format) { case JsonFormatStrings.Byte: - return "(byte)" + Convert.ToByte(value).ToString(CultureInfo.InvariantCulture); + return "(byte)" + Convert.ToByte(value, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); case JsonFormatStrings.Integer: - return Convert.ToInt32(value).ToString(CultureInfo.InvariantCulture); + return Convert.ToInt32(value, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture); case JsonFormatStrings.Long: - return Convert.ToInt64(value) + "L"; + return Convert.ToInt64(value, CultureInfo.InvariantCulture) + "L"; case JsonFormatStrings.Double: return ConvertNumberToString(value) + "D"; case JsonFormatStrings.Float: diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs b/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs index ef3983f1b..7c44e0ada 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/Models/ClassTemplateModel.cs @@ -6,6 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System; using System.Collections.Generic; using System.Linq; using NJsonSchema.CodeGeneration.Models; @@ -145,7 +146,7 @@ public ClassTemplateModel(string typeName, CSharpGeneratorSettings settings, _schema?.InheritsSchema(_resolver.ExceptionSchema) == true; /// Gets a value indicating whether to use the DateFormatConverter. - public bool UseDateFormatConverter => _settings.DateType.StartsWith("System.DateTime"); + public bool UseDateFormatConverter => _settings.DateType.StartsWith("System.DateTime", StringComparison.Ordinal); /// Gets or sets the access modifier of generated classes and interfaces. public string TypeAccessModifier => _settings.TypeAccessModifier; diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Models/EnumTemplateModel.cs b/src/NJsonSchema.CodeGeneration.CSharp/Models/EnumTemplateModel.cs index efc2698bb..0855e9936 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Models/EnumTemplateModel.cs +++ b/src/NJsonSchema.CodeGeneration.CSharp/Models/EnumTemplateModel.cs @@ -8,6 +8,7 @@ using NJsonSchema.Annotations; using System.Collections.Generic; +using System.Globalization; using System.Linq; using NJsonSchema.CodeGeneration.Models; @@ -78,8 +79,8 @@ public IEnumerable Enums { Name = _settings.EnumNameGenerator.Generate(i, name, value, _schema), Value = value.ToString(), - InternalValue = valueInt64.ToString(), - InternalFlagValue = valueInt64.ToString() + InternalValue = valueInt64.ToString(CultureInfo.InvariantCulture), + InternalFlagValue = valueInt64.ToString(CultureInfo.InvariantCulture) }); } else @@ -89,7 +90,7 @@ public IEnumerable Enums Name = _settings.EnumNameGenerator.Generate(i, name, value, _schema), Value = value.ToString(), InternalValue = value.ToString(), - InternalFlagValue = (1 << i).ToString() + InternalFlagValue = (1 << i).ToString(CultureInfo.InvariantCulture) }); } } @@ -102,8 +103,8 @@ public IEnumerable Enums { Name = _settings.EnumNameGenerator.Generate(i, name, value, _schema), Value = value.ToString(), - InternalValue = i.ToString(), - InternalFlagValue = (1 << i).ToString() + InternalValue = i.ToString(CultureInfo.InvariantCulture), + InternalFlagValue = (1 << i).ToString(CultureInfo.InvariantCulture) }); } } diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.Annotations.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.Annotations.liquid new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.Annotations.liquid @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.Member.Annotations.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.Member.Annotations.liquid new file mode 100644 index 000000000..5f282702b --- /dev/null +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.Member.Annotations.liquid @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid index 849187e90..57956c084 100644 --- a/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid +++ b/src/NJsonSchema.CodeGeneration.CSharp/Templates/Enum.liquid @@ -7,12 +7,14 @@ {%- if IsEnumAsBitFlags -%} [System.Flags] {%- endif -%} +{%- template Enum.Annotations -%} {{ TypeAccessModifier }} enum {{ Name }}{%- if HasExtendedValueRange %} : long{% endif %} { {%- for enum in Enums %} {%- if IsStringEnum -%} [System.Runtime.Serialization.EnumMember(Value = @"{{ enum.Value | replace: '"', '""' }}")] {%- endif -%} +{%- template Enum.Member.Annotations -%} {%- if IsEnumAsBitFlags -%} {{ enum.Name }} = {{ enum.InternalFlagValue }}, {%- else -%} diff --git a/src/NJsonSchema.CodeGeneration.Tests/NJsonSchema.CodeGeneration.Tests.csproj b/src/NJsonSchema.CodeGeneration.Tests/NJsonSchema.CodeGeneration.Tests.csproj index 08c8252c0..641305b15 100644 --- a/src/NJsonSchema.CodeGeneration.Tests/NJsonSchema.CodeGeneration.Tests.csproj +++ b/src/NJsonSchema.CodeGeneration.Tests/NJsonSchema.CodeGeneration.Tests.csproj @@ -5,6 +5,7 @@ false $(NoWarn),1998,1591,618 disable + false diff --git a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NJsonSchema.CodeGeneration.TypeScript.Tests.csproj b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NJsonSchema.CodeGeneration.TypeScript.Tests.csproj index 356f35ade..c2dc07531 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NJsonSchema.CodeGeneration.TypeScript.Tests.csproj +++ b/src/NJsonSchema.CodeGeneration.TypeScript.Tests/NJsonSchema.CodeGeneration.TypeScript.Tests.csproj @@ -6,6 +6,7 @@ true $(NoWarn),1587,1998,1591,618 disable + false diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/DataConversionGenerator.cs b/src/NJsonSchema.CodeGeneration.TypeScript/DataConversionGenerator.cs index ce5923a35..672bcd731 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/DataConversionGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/DataConversionGenerator.cs @@ -147,7 +147,7 @@ private static string GetStringToDateTime(DataConversionParameters parameters, J return "dayjs"; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(parameters)); } } @@ -168,7 +168,7 @@ private static string GetDateToString(DataConversionParameters parameters, JsonS return "toFormat('yyyy-MM-dd')"; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(parameters)); } } @@ -207,7 +207,7 @@ private static string GetDateTimeToString(DataConversionParameters parameters, J return "toISOString()"; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(parameters)); } } diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/Models/ClassTemplateModel.cs b/src/NJsonSchema.CodeGeneration.TypeScript/Models/ClassTemplateModel.cs index 48ce8df29..963206f81 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/Models/ClassTemplateModel.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/Models/ClassTemplateModel.cs @@ -104,7 +104,7 @@ public string Inheritance public bool GenerateConstructorInterface => _settings.GenerateConstructorInterface; /// Gets or sets a value indicating whether POJO objects in the constructor data are converted to DTO instances (default: true). - public bool ConvertConstructorInterfaceData => _settings.ConvertConstructorInterfaceData && Properties.Any(p => p.SupportsConstructorConversion); + public bool ConvertConstructorInterfaceData => _settings.ConvertConstructorInterfaceData && Properties.Exists(p => p.SupportsConstructorConversion); /// Gets the null value. public string NullValue => _settings.NullValue.ToString().ToLowerInvariant(); @@ -134,13 +134,13 @@ public string IndexerPropertyValueType public bool HandleReferences => _settings.HandleReferences; /// Gets a value indicating whether the type has properties. - public bool HasProperties => Properties.Any(); + public bool HasProperties => Properties.Count > 0; /// Gets the property models. public List Properties { get; } /// Gets a value indicating whether any property has a default value. - public bool HasDefaultValues => Properties.Any(p => p.HasDefaultValue); + public bool HasDefaultValues => Properties.Exists(p => p.HasDefaultValue); /// Gets a value indicating whether public bool RequiresStrictPropertyInitialization => _settings.RequiresStrictPropertyInitialization; diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptGenerator.cs b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptGenerator.cs index 2b74f1f10..f6f5c9a0b 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptGenerator.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptGenerator.cs @@ -146,7 +146,9 @@ protected override CodeArtifact GenerateType(JsonSchema schema, string typeNameH } else { +#pragma warning disable CA2208 throw new ArgumentOutOfRangeException(nameof(Settings.EnumStyle), Settings.EnumStyle, "Unknown enum style"); +#pragma warning restore CA2208 } var template = Settings.TemplateFactory.CreateTemplate("TypeScript", templateName, model); diff --git a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs index b1821edf1..b61f02fd2 100644 --- a/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs +++ b/src/NJsonSchema.CodeGeneration.TypeScript/TypeScriptTypeResolver.cs @@ -56,7 +56,9 @@ public override string Resolve(JsonSchema schema, bool isNullable, string? typeN /// Gets a value indicating whether the schema supports constructor conversion. /// The schema. /// The result. +#pragma warning disable CA1822 public bool SupportsConstructorConversion(JsonSchema? schema) +#pragma warning restore CA1822 { return schema?.ActualSchema.ResponsibleDiscriminatorObject == null; } @@ -109,7 +111,7 @@ private string Resolve(JsonSchema schema, string? typeNameHint, bool addInterfac if (type.IsInteger() && !schema.ActualTypeSchema.IsEnumeration) { - return ResolveInteger(schema.ActualTypeSchema, typeNameHint); + return TypeScriptTypeResolver.ResolveInteger(schema.ActualTypeSchema, typeNameHint); } if (type.IsBoolean()) @@ -167,7 +169,9 @@ private string Resolve(JsonSchema schema, string? typeNameHint, bool addInterfac return $"{{ [key in {keyType}]?: {valueType}; }}"; } +#pragma warning disable CA2208 throw new ArgumentOutOfRangeException(nameof(Settings.EnumStyle), Settings.EnumStyle, "Unknown enum style"); +#pragma warning restore CA2208 } return $"{{ [key: {keyType}]: {valueType}; }}"; @@ -290,7 +294,7 @@ private string ResolveString(JsonSchema schema, string? typeNameHint) return "string"; } - private string ResolveInteger(JsonSchema schema, string? typeNameHint) + private static string ResolveInteger(JsonSchema schema, string? typeNameHint) { return "number"; } diff --git a/src/NJsonSchema.CodeGeneration/CodeArtifactExtensions.cs b/src/NJsonSchema.CodeGeneration/CodeArtifactExtensions.cs index ddc19fc96..e53f6f4e3 100644 --- a/src/NJsonSchema.CodeGeneration/CodeArtifactExtensions.cs +++ b/src/NJsonSchema.CodeGeneration/CodeArtifactExtensions.cs @@ -6,6 +6,7 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System; using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; @@ -69,7 +70,7 @@ public static IEnumerable OrderByBaseDependency(this IEnumerableRico Suter, mail@rsuter.com //----------------------------------------------------------------------- +using System; using System.Text.RegularExpressions; namespace NJsonSchema.CodeGeneration @@ -29,48 +30,37 @@ public string Generate(int index, string? name, object? value, JsonSchema schema return "Empty"; } - switch (name) + name = name switch { - case ("="): - name = "Eq"; - break; - case ("!="): - name = "Ne"; - break; - case (">"): - name = "Gt"; - break; - case ("<"): - name = "Lt"; - break; - case (">="): - name = "Ge"; - break; - case ("<="): - name = "Le"; - break; - case ("~="): - name = "Approx"; - break; - } + "=" => "Eq", + "!=" => "Ne", + ">" => "Gt", + "<" => "Lt", + ">=" => "Ge", + "<=" => "Le", + "~=" => "Approx", + _ => name + }; - if (name!.StartsWith("-")) +#pragma warning disable CS8604 // Possible null reference argument. + if (name.StartsWith('-')) +#pragma warning restore CS8604 // Possible null reference argument. { name = "Minus" + name.Substring(1); } - if (name.StartsWith("+")) + if (name.StartsWith('+')) { name = "Plus" + name.Substring(1); } - if (name.StartsWith("_-")) + if (name.StartsWith("_-", StringComparison.Ordinal)) { name = "__" + name.Substring(2); } return _invalidNameCharactersPattern.Replace(ConversionUtilities.ConvertToUpperCamelCase(name - .Replace(":", "-").Replace(@"""", @""), true), "_"); + .Replace(":", "-").Replace(@"""", ""), firstCharacterMustBeAlpha: true), "_"); } } } diff --git a/src/NJsonSchema.CodeGeneration/DefaultTemplateFactory.cs b/src/NJsonSchema.CodeGeneration/DefaultTemplateFactory.cs index 348ccdb9c..2fc559c63 100644 --- a/src/NJsonSchema.CodeGeneration/DefaultTemplateFactory.cs +++ b/src/NJsonSchema.CodeGeneration/DefaultTemplateFactory.cs @@ -103,7 +103,7 @@ protected virtual string GetEmbeddedLiquidTemplate(string language, string templ /// Could not load template. private string GetLiquidTemplate(string language, string template) { - if (!template.EndsWith("!") && !string.IsNullOrEmpty(_settings.TemplateDirectory)) + if (!template.EndsWith('!') && !string.IsNullOrEmpty(_settings.TemplateDirectory)) { var templateFilePath = Path.Combine(_settings.TemplateDirectory, template + ".liquid"); if (File.Exists(templateFilePath)) diff --git a/src/NJsonSchema.CodeGeneration/ExtensionCode.cs b/src/NJsonSchema.CodeGeneration/ExtensionCode.cs index cbcba205d..cbea11e2f 100644 --- a/src/NJsonSchema.CodeGeneration/ExtensionCode.cs +++ b/src/NJsonSchema.CodeGeneration/ExtensionCode.cs @@ -33,12 +33,12 @@ public abstract class ExtensionCode /// The extension class is not defined. public string GetExtensionClassBody(string className) { - if (!ExtensionClasses.ContainsKey(className)) + if (!ExtensionClasses.TryGetValue(className, out string? value)) { throw new InvalidOperationException("The extension class '" + className + "' is not defined."); } - var match = Regex.Match(ExtensionClasses[className], "(.*?)class (.*?){(.*)}", RegexOptions.Singleline); + var match = Regex.Match(value, "(.*?)class (.*?){(.*)}", RegexOptions.Singleline); return match.Groups[3].Value; } } diff --git a/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj b/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj index b653ffa10..209852fbf 100644 --- a/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj +++ b/src/NJsonSchema.CodeGeneration/NJsonSchema.CodeGeneration.csproj @@ -21,9 +21,8 @@ - - EnumExtensions.cs - + + \ No newline at end of file diff --git a/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs b/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs index 5225df649..b390262ba 100644 --- a/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs +++ b/src/NJsonSchema.CodeGeneration/ValueGeneratorBase.cs @@ -6,7 +6,6 @@ // Rico Suter, mail@rsuter.com //----------------------------------------------------------------------- -using NJsonSchema.Annotations; using System.Collections.Generic; using System.Globalization; using System.Linq; @@ -18,7 +17,7 @@ namespace NJsonSchema.CodeGeneration public abstract class ValueGeneratorBase { private readonly CodeGeneratorSettingsBase _settings; - private readonly List _unsupportedFormatStrings = new List() + private readonly HashSet _unsupportedFormatStrings = new() { JsonFormatStrings.Date, JsonFormatStrings.DateTime, @@ -110,7 +109,9 @@ protected virtual string GetEnumDefaultValue(JsonSchema schema, JsonSchema actua /// Gets the default value as string literal. /// The schema. /// The string literal. +#pragma warning disable CA1822 protected string GetDefaultAsStringLiteral(JsonSchema schema) +#pragma warning restore CA1822 { return "\"" + ConversionUtilities.ConvertToStringLiteral(schema.Default?.ToString() ?? string.Empty) + "\""; } @@ -118,7 +119,9 @@ protected string GetDefaultAsStringLiteral(JsonSchema schema) /// Converts a number to its string representation. /// The value. /// The string. +#pragma warning disable CA1822 protected string ConvertNumberToString(object value) +#pragma warning restore CA1822 { if (value is byte) { diff --git a/src/NJsonSchema.NewtonsoftJson/Converters/JsonExceptionConverter.cs b/src/NJsonSchema.NewtonsoftJson/Converters/JsonExceptionConverter.cs index 4e0fe72a3..e68a8a3ea 100644 --- a/src/NJsonSchema.NewtonsoftJson/Converters/JsonExceptionConverter.cs +++ b/src/NJsonSchema.NewtonsoftJson/Converters/JsonExceptionConverter.cs @@ -67,7 +67,7 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer if (value is not null) { - foreach (var property in GetExceptionProperties(value.GetType())) + foreach (var property in JsonExceptionConverter.GetExceptionProperties(value.GetType())) { var propertyValue = property.Key.GetValue(exception); if (propertyValue != null) @@ -109,7 +109,7 @@ public override bool CanConvert(Type objectType) var newSerializer = new JsonSerializer(); newSerializer.ContractResolver = (IContractResolver)Activator.CreateInstance(serializer.ContractResolver.GetType()); - var field = GetField(typeof(DefaultContractResolver), "_sharedCache"); + var field = JsonExceptionConverter.GetField(typeof(DefaultContractResolver), "_sharedCache"); if (field != null) { field.SetValue(newSerializer.ContractResolver, false); @@ -130,7 +130,7 @@ public override bool CanConvert(Type objectType) if (jObject.TryGetValue("discriminator", StringComparison.OrdinalIgnoreCase, out token)) { var discriminator = token.Value(); - if (objectType.Name.Equals(discriminator) == false) + if (objectType.Name.Equals(discriminator, StringComparison.Ordinal) == false) { var exceptionType = Type.GetType("System." + discriminator, false); if (exceptionType != null) @@ -155,7 +155,7 @@ public override bool CanConvert(Type objectType) var value = jObject.ToObject(objectType, newSerializer); if (value is not null) { - foreach (var property in GetExceptionProperties(value.GetType())) + foreach (var property in JsonExceptionConverter.GetExceptionProperties(value.GetType())) { var jValue = jObject.GetValue(resolver.GetResolvedPropertyName(property.Value)); var propertyValue = (object?)jValue?.ToObject(property.Key.PropertyType); @@ -167,14 +167,14 @@ public override bool CanConvert(Type objectType) { var fieldNameSuffix = property.Value.Substring(0, 1).ToLowerInvariant() + property.Value.Substring(1); - field = GetField(objectType, "m_" + fieldNameSuffix); + field = JsonExceptionConverter.GetField(objectType, "m_" + fieldNameSuffix); if (field != null) { field.SetValue(value, propertyValue); } else { - field = GetField(objectType, "_" + fieldNameSuffix); + field = JsonExceptionConverter.GetField(objectType, "_" + fieldNameSuffix); if (field != null) { field.SetValue(value, propertyValue); @@ -192,19 +192,25 @@ public override bool CanConvert(Type objectType) return value; } - private FieldInfo? GetField(Type type, string fieldName) + private static FieldInfo? GetField(Type type, string fieldName) { var typeInfo = type.GetTypeInfo(); var field = typeInfo.GetDeclaredField(fieldName); if (field == null && typeInfo.BaseType != null) { - return GetField(typeInfo.BaseType, fieldName); + return JsonExceptionConverter.GetField(typeInfo.BaseType, fieldName); } return field; } - private IDictionary GetExceptionProperties(Type exceptionType) + + private static readonly HashSet ignoredExceptionProperties = new() + { + "Message", "StackTrace", "Source", "InnerException", "Data", "TargetSite", "HelpLink", "HResult" + }; + + private static Dictionary GetExceptionProperties(Type exceptionType) { var result = new Dictionary(); foreach (var property in exceptionType.GetRuntimeProperties().Where(p => p.GetMethod?.IsPublic == true)) @@ -212,8 +218,7 @@ private IDictionary GetExceptionProperties(Type exceptionT var attribute = property.GetCustomAttribute(); var propertyName = attribute != null ? attribute.PropertyName : property.Name; - if (propertyName is not null && - !new[] { "Message", "StackTrace", "Source", "InnerException", "Data", "TargetSite", "HelpLink", "HResult" }.Contains(propertyName)) + if (propertyName is not null && !ignoredExceptionProperties.Contains(propertyName)) { result[property] = propertyName; } @@ -221,7 +226,7 @@ private IDictionary GetExceptionProperties(Type exceptionT return result; } - private void SetExceptionFieldValue(JObject jObject, string propertyName, object value, string fieldName, IContractResolver resolver, JsonSerializer serializer) + private static void SetExceptionFieldValue(JObject jObject, string propertyName, object value, string fieldName, IContractResolver resolver, JsonSerializer serializer) { var field = typeof(Exception).GetTypeInfo().GetDeclaredField(fieldName); var jsonPropertyName = resolver is DefaultContractResolver ? ((DefaultContractResolver)resolver).GetResolvedPropertyName(propertyName) : propertyName; diff --git a/src/NJsonSchema.NewtonsoftJson/Converters/JsonInheritanceConverter.cs b/src/NJsonSchema.NewtonsoftJson/Converters/JsonInheritanceConverter.cs index 9b31a8dbc..aff50579c 100644 --- a/src/NJsonSchema.NewtonsoftJson/Converters/JsonInheritanceConverter.cs +++ b/src/NJsonSchema.NewtonsoftJson/Converters/JsonInheritanceConverter.cs @@ -250,7 +250,7 @@ protected virtual Type GetDiscriminatorType(JObject jObject, Type objectType, st throw new InvalidOperationException("Could not find subtype of '" + objectType.Name + "' with discriminator '" + discriminatorValue + "'."); } - private Type? GetSubtypeFromKnownTypeAttributes(Type objectType, string discriminator) + private static Type? GetSubtypeFromKnownTypeAttributes(Type objectType, string discriminator) { var type = objectType; do @@ -265,10 +265,10 @@ protected virtual Type GetDiscriminatorType(JObject jObject, Type objectType, st } else if (attribute.MethodName != null) { - var method = type.GetRuntimeMethod((string)attribute.MethodName, new Type[0]); + var method = type.GetRuntimeMethod((string)attribute.MethodName, Type.EmptyTypes); if (method != null) { - var types = (System.Collections.Generic.IEnumerable)method.Invoke(null, new object[0]); + var types = (System.Collections.Generic.IEnumerable)method.Invoke(null, Array.Empty()); foreach (var knownType in types) { if (knownType.Name == discriminator) diff --git a/src/NJsonSchema.NewtonsoftJson/Converters/JsonReferenceConverter.cs b/src/NJsonSchema.NewtonsoftJson/Converters/JsonReferenceConverter.cs index 5a91833c7..68de3aa08 100644 --- a/src/NJsonSchema.NewtonsoftJson/Converters/JsonReferenceConverter.cs +++ b/src/NJsonSchema.NewtonsoftJson/Converters/JsonReferenceConverter.cs @@ -17,7 +17,7 @@ public class JsonReferenceConverter : JsonConverter // TODO: Use converter for JsonSchema4! [ThreadStatic] - private static bool _isWriting = false; + private static bool _isWriting; /// Gets a value indicating whether this can write JSON. public override bool CanWrite => !_isWriting; diff --git a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonExtensionDataGenerationTests.cs b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonExtensionDataGenerationTests.cs index 0ebc39939..0a947d1c4 100644 --- a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonExtensionDataGenerationTests.cs +++ b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonExtensionDataGenerationTests.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using NJsonSchema.Generation; @@ -24,6 +25,56 @@ public class ClassWithJsonElementExtensionData public IDictionary ExtensionData { get; set; } } + public class ClassWithIndexedProperty + { + public double X { get; set; } + public double Y { get; set; } + + public double this[int indexer] + { + get + { + switch (indexer) + { + case 0: return X; + case 1: return Y; + default: throw new ArgumentOutOfRangeException(nameof(indexer)); + } + } + set + { + switch (indexer) + { + case 0: X = value; break; + case 1: Y = value; break; + default: throw new ArgumentOutOfRangeException(nameof(indexer)); + } + } + } + + public double this[string indexer] + { + get + { + switch (indexer) + { + case "X": return X; + case "Y": return Y; + default: throw new ArgumentOutOfRangeException(nameof(indexer)); + } + } + set + { + switch (indexer) + { + case "X": X = value; break; + case "Y": Y = value; break; + default: throw new ArgumentOutOfRangeException(nameof(indexer)); + } + } + } + } + [Fact] public void SystemTextJson_When_class_has_object_Dictionary_with_JsonExtensionDataAttribute_on_property_then_AdditionalProperties_schema_is_set() { @@ -53,5 +104,19 @@ public void SystemTextJson_When_class_has_JsonElement_Dictionary_with_JsonExtens Assert.True(schema.AllowAdditionalProperties); Assert.True(schema.AdditionalPropertiesSchema.ActualSchema.IsAnyType); } + + [Fact] + public void SystemTextJson_When_class_has_Indexed_properties_then_Generates_schema_without_them() + { + // Act + var schema = JsonSchemaGenerator.FromType(new SystemTextJsonSchemaGeneratorSettings + { + SchemaType = SchemaType.JsonSchema + }); + + // Assert + Assert.Equal(2, schema.ActualProperties.Count); + Assert.All(schema.ActualProperties, property => Assert.False(string.Equals(property.Key, "Item", StringComparison.InvariantCultureIgnoreCase))); + } } } \ No newline at end of file diff --git a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonTests.cs b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonTests.cs index 530d06014..83984abbc 100644 --- a/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonTests.cs +++ b/src/NJsonSchema.Tests/Generation/SystemTextJson/SystemTextJsonTests.cs @@ -14,6 +14,14 @@ public class HealthCheckResult public string Name { get; } public string Description { get; } + + private string PrivateReadOnlyProperty1 { get; } + + private string PrivateReadOnlyProperty2 => "TEST"; + + public static string PublicReadOnlyStaticProperty { get; } + + private static string PrivateReadOnlyStaticProperty { get; } } [Fact] @@ -81,5 +89,31 @@ public async Task When_type_is_excluded_with_json_schema_ignore_attribute_then_i Assert.DoesNotContain(@"NestedType2", data); Assert.Contains(@"Property", data); } + + [Fact] + public async Task When_property_is_private_and_readonly_then_its_not_in_the_schema() + { + //// Act + var schema = JsonSchema.FromType(); + var data = schema.ToJson(); + + //// Assert + Assert.NotNull(data); + Assert.False(data.Contains("PrivateReadOnlyProperty1"), data); + Assert.False(data.Contains("PrivateReadOnlyProperty2"), data); + } + + [Fact] + public async Task When_property_is_static_readonly_then_its_not_in_the_schema() + { + //// Act + var schema = JsonSchema.FromType(); + var data = schema.ToJson(); + + //// Assert + Assert.NotNull(data); + Assert.False(data.Contains("PublicReadOnlyStaticProperty"), data); + Assert.False(data.Contains("PrivateReadOnlyStaticProperty"), data); + } } } diff --git a/src/NJsonSchema.Tests/Validation/ObjectValidationTests.cs b/src/NJsonSchema.Tests/Validation/ObjectValidationTests.cs index b74268380..1cae72fc5 100644 --- a/src/NJsonSchema.Tests/Validation/ObjectValidationTests.cs +++ b/src/NJsonSchema.Tests/Validation/ObjectValidationTests.cs @@ -24,6 +24,7 @@ public void When_token_is_not_object_then_validation_should_fail() //// Assert Assert.Equal(ValidationErrorKind.ObjectExpected, errors.First().Kind); + Assert.Equal("10", errors.First().Token?.ToString()); } [Fact] @@ -47,6 +48,7 @@ public void When_required_property_is_missing_then_it_should_be_in_error_list() Assert.Equal("Foo", errors.First().Property); Assert.Equal("#/Foo", errors.First().Path); Assert.Equal(ValidationErrorKind.PropertyRequired, errors.First().Kind); + Assert.Equal("{}", errors.First().Token?.ToString()); } [Fact] @@ -134,6 +136,7 @@ public void When_string_property_required_but_integer_provided_then_it_should_fa Assert.Equal(ValidationErrorKind.StringExpected, errors.First().Kind); Assert.Equal("Foo", errors.First().Property); Assert.Equal("#/Foo", errors.First().Path); + Assert.Equal("10", errors.First().Token?.ToString()); } [Fact] @@ -180,6 +183,7 @@ public void When_case_sensitive_and_property_has_different_casing_then_it_should //// Assert Assert.Equal(ValidationErrorKind.NoAdditionalPropertiesAllowed, errors.First().Kind); + Assert.Equal("\"foo\": 5", errors.First().Token?.ToString()); } [Fact] diff --git a/src/NJsonSchema/Collections/ObservableDictionary.cs b/src/NJsonSchema/Collections/ObservableDictionary.cs index fdcd8f4de..ce3315004 100644 --- a/src/NJsonSchema/Collections/ObservableDictionary.cs +++ b/src/NJsonSchema/Collections/ObservableDictionary.cs @@ -75,7 +75,7 @@ public void AddRange(IDictionary items) { if (items == null) { - throw new ArgumentNullException("items"); + throw new ArgumentNullException(nameof(items)); } if (items.Count > 0) @@ -183,10 +183,10 @@ private void OnCollectionChanged(NotifyCollectionChangedAction action, IList new private void OnPropertyChanged() { - OnPropertyChanged("Count"); + OnPropertyChanged(nameof(Count)); OnPropertyChanged("Item[]"); - OnPropertyChanged("Keys"); - OnPropertyChanged("Values"); + OnPropertyChanged(nameof(Keys)); + OnPropertyChanged(nameof(Values)); } #region IDictionary interface @@ -213,7 +213,7 @@ public bool Remove(TKey key) { if (key == null) { - throw new ArgumentNullException("key"); + throw new ArgumentNullException(nameof(key)); } TValue? value; diff --git a/src/NJsonSchema/ConversionUtilities.cs b/src/NJsonSchema/ConversionUtilities.cs index 1aa9fe006..ec61f5474 100644 --- a/src/NJsonSchema/ConversionUtilities.cs +++ b/src/NJsonSchema/ConversionUtilities.cs @@ -138,7 +138,7 @@ public static string ConvertToStringLiteral(string input) else { literal.Append(@"\u"); - literal.Append(((int) c).ToString("x4")); + literal.Append(((int) c).ToString("x4", CultureInfo.InvariantCulture)); } break; @@ -197,7 +197,7 @@ public static string Singularize(string word) return "Person"; } - return word.EndsWith("s") ? word.Substring(0, word.Length - 1) : word; + return word.EndsWith('s') ? word.Substring(0, word.Length - 1) : word; } /// Add tabs to the given string. @@ -303,7 +303,7 @@ private static string CreateTabString(int tabCount) private static string ConvertDashesToCamelCase(string input) { - if (input.IndexOf('-') == -1) + if (!input.Contains('-')) { // no conversion necessary return input; diff --git a/src/NJsonSchema/Converters/JsonInheritanceConverter.cs b/src/NJsonSchema/Converters/JsonInheritanceConverter.cs index bdd5b6d78..9d72b4e40 100644 --- a/src/NJsonSchema/Converters/JsonInheritanceConverter.cs +++ b/src/NJsonSchema/Converters/JsonInheritanceConverter.cs @@ -43,7 +43,9 @@ public JsonInheritanceConverterAttribute(Type baseType, string discriminatorName public class JsonInheritanceConverter : JsonConverter { /// Gets the list of additional known types. +#pragma warning disable CA1000 public static IDictionary AdditionalKnownTypes { get; } = new Dictionary(); +#pragma warning restore CA1000 private readonly string _discriminatorName; @@ -132,9 +134,9 @@ protected virtual Type GetDiscriminatorType(JsonElement jObject, Type objectType { if (discriminatorValue != null) { - if (AdditionalKnownTypes.ContainsKey(discriminatorValue)) + if (AdditionalKnownTypes.TryGetValue(discriminatorValue, out Type? value)) { - return AdditionalKnownTypes[discriminatorValue]; + return value; } var jsonInheritanceAttributeSubtype = GetObjectSubtype(objectType, discriminatorValue); @@ -183,11 +185,10 @@ protected virtual Type GetDiscriminatorType(JsonElement jObject, Type objectType } else if (attribute.MethodName != null) { - var method = type.GetRuntimeMethod((string)attribute.MethodName, new Type[0]); + var method = type.GetRuntimeMethod((string)attribute.MethodName, Type.EmptyTypes); if (method != null) { - var types = method.Invoke(null, new object[0]) as IEnumerable; - if (types != null) + if (method.Invoke(null, Array.Empty()) is IEnumerable types) { foreach (var knownType in types) { diff --git a/src/NJsonSchema/DefaultTypeNameGenerator.cs b/src/NJsonSchema/DefaultTypeNameGenerator.cs index 417270ead..cd29eae0a 100644 --- a/src/NJsonSchema/DefaultTypeNameGenerator.cs +++ b/src/NJsonSchema/DefaultTypeNameGenerator.cs @@ -62,12 +62,14 @@ public virtual string Generate(JsonSchema schema, string? typeNameHint, IEnumera } var typeName = Generate(schema, typeNameHint); + typeName = RemoveIllegalCharacters(typeName); + if (string.IsNullOrEmpty(typeName) || reservedTypeNames.Contains(typeName)) { typeName = GenerateAnonymousTypeName(typeNameHint, reservedTypeNames); } - return RemoveIllegalCharacters(typeName); + return typeName; } /// Generates the type name for the given schema. diff --git a/src/NJsonSchema/Generation/JsonSchemaGenerator.cs b/src/NJsonSchema/Generation/JsonSchemaGenerator.cs index 4dac96e46..f973f87b3 100644 --- a/src/NJsonSchema/Generation/JsonSchemaGenerator.cs +++ b/src/NJsonSchema/Generation/JsonSchemaGenerator.cs @@ -16,6 +16,7 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.Serialization; @@ -147,7 +148,7 @@ public virtual void Generate(TSchemaType schema, ContextualType con { var typeDescription = Settings.ReflectionService.GetDescription(contextualType, Settings); - ApplyTypeExtensionDataAttributes(schema, contextualType); + JsonSchemaGenerator.ApplyTypeExtensionDataAttributes(schema, contextualType); if (TryHandleSpecialTypes(schema, typeDescription.ContextualType, schemaResolver)) { @@ -384,7 +385,7 @@ public virtual void ApplyDataAnnotations(JsonSchema schema, JsonTypeDescription if (typeDescription.Type == JsonObjectType.Number || typeDescription.Type == JsonObjectType.Integer) { - ApplyRangeAttribute(schema, contextualType.GetContextAttributes(true)); + JsonSchemaGenerator.ApplyRangeAttribute(schema, contextualType.GetContextAttributes(true)); var multipleOfAttribute = contextualType.GetContextAttribute(true); if (multipleOfAttribute != null) @@ -504,7 +505,7 @@ public virtual void ApplyDataAnnotations(JsonSchema schema, JsonTypeDescription return null; } - private object? GenerateExample(string xmlDocs) + private static object? GenerateExample(string xmlDocs) { try { @@ -707,7 +708,7 @@ protected virtual void GenerateEnum(JsonSchema schema, JsonTypeDescription typeD { if (typeDescription.Type == JsonObjectType.Integer) { - var value = Convert.ChangeType(Enum.Parse(contextualType.Type, enumName), underlyingType); + var value = Convert.ChangeType(Enum.Parse(contextualType.Type, enumName), underlyingType, CultureInfo.InvariantCulture); schema.Enumeration.Add(value); } else @@ -866,7 +867,9 @@ private void GenerateEnum( /// /// /// +#pragma warning disable CA1822 public bool IsAbstractProperty(ContextualMemberInfo memberInfo) +#pragma warning restore CA1822 { return memberInfo is ContextualPropertyInfo propertyInfo && propertyInfo.PropertyInfo.DeclaringType?.GetTypeInfo().IsInterface == false && @@ -893,7 +896,7 @@ private void GenerateKnownTypes(Type type, JsonSchemaResolver schemaResolver) } else if (attribute.MethodName != null) { - var methodInfo = type.GetRuntimeMethod((string)attribute.MethodName, new Type[0]); + var methodInfo = type.GetRuntimeMethod((string)attribute.MethodName, Type.EmptyTypes); if (methodInfo != null) { var knownTypes = methodInfo.Invoke(null, null) as IEnumerable; @@ -1086,6 +1089,7 @@ private void GenerateInheritanceDiscriminator(Type type, JsonSchema schema, Json } } +#pragma warning disable CA1859 private object? TryGetInheritanceDiscriminatorConverter(Type type) { var typeAttributes = type.GetTypeInfo().GetCustomAttributes(false).OfType(); @@ -1096,8 +1100,8 @@ private void GenerateInheritanceDiscriminator(Type type, JsonSchema schema, Json { var converterType = (Type)jsonConverterAttribute.ConverterType; if (converterType != null && ( - converterType.IsAssignableToTypeName("JsonInheritanceConverter", TypeNameStyle.Name) || // Newtonsoft's converter - converterType.IsAssignableToTypeName("JsonInheritanceConverter`1", TypeNameStyle.Name) // System.Text.Json's converter + converterType.IsAssignableToTypeName("JsonInheritanceConverter", TypeNameStyle.Name) || // Newtonsoft's converter + converterType.IsAssignableToTypeName("JsonInheritanceConverter`1", TypeNameStyle.Name) // System.Text.Json's converter )) { return ObjectExtensions.HasProperty(jsonConverterAttribute, "ConverterParameters") && @@ -1113,7 +1117,7 @@ private void GenerateInheritanceDiscriminator(Type type, JsonSchema schema, Json .Where(a => a.GetType().IsAssignableToTypeName("System.Text.Json.Serialization.JsonDerivedTypeAttribute", TypeNameStyle.FullName)) .ToArray(); - if (jsonDerivedTypeAttributes.Any()) + if (jsonDerivedTypeAttributes.Length > 0) { dynamic? jsonPolymorphicAttribute = typeAttributes .FirstAssignableToTypeNameOrDefault("System.Text.Json.Serialization.JsonPolymorphicAttribute", TypeNameStyle.FullName); @@ -1122,8 +1126,10 @@ private void GenerateInheritanceDiscriminator(Type type, JsonSchema schema, Json return null; } +#pragma warning restore CA1859 - private class SystemTextJsonInheritanceWrapper + + private sealed class SystemTextJsonInheritanceWrapper { private readonly dynamic[] _jsonDerivedTypeAttributes; @@ -1142,7 +1148,7 @@ public string GetDiscriminatorValue(Type type) } } - private string? TryGetInheritanceDiscriminatorName(object jsonInheritanceConverter) + private static string? TryGetInheritanceDiscriminatorName(object jsonInheritanceConverter) { return ObjectExtensions.TryGetPropertyValue( jsonInheritanceConverter, @@ -1151,7 +1157,7 @@ public string GetDiscriminatorValue(Type type) } /// - /// + /// /// /// /// @@ -1219,7 +1225,7 @@ public void AddProperty( propertySchema.Default = ConvertDefaultValue(property.AccessorType, defaultValue); ApplyDataAnnotations(propertySchema, propertyTypeDescription); - ApplyPropertyExtensionDataAttributes(propertySchema, property); + JsonSchemaGenerator.ApplyPropertyExtensionDataAttributes(propertySchema, property); }; var referencingProperty = GenerateWithReferenceAndNullability( @@ -1250,7 +1256,7 @@ public virtual bool IsPropertyIgnored(ContextualAccessorInfo accessorInfo, Type } /// - /// + /// /// /// /// @@ -1271,12 +1277,14 @@ public bool IsPropertyIgnoredBySettings(ContextualAccessorInfo accessorInfo) } /// - /// + /// /// /// /// /// +#pragma warning disable CA1822 public dynamic? GetDataMemberAttribute(ContextualAccessorInfo accessorInfo, Type parentType) +#pragma warning restore CA1822 { if (!HasDataContractAttribute(parentType)) { @@ -1286,14 +1294,14 @@ public bool IsPropertyIgnoredBySettings(ContextualAccessorInfo accessorInfo) return accessorInfo.GetAttributes(true).FirstAssignableToTypeNameOrDefault("DataMemberAttribute", TypeNameStyle.Name); } - private bool HasDataContractAttribute(Type parentType) + private static bool HasDataContractAttribute(Type parentType) { return parentType.ToCachedType() .GetAttributes(true) .FirstAssignableToTypeNameOrDefault("DataContractAttribute", TypeNameStyle.Name) != null; } - private void ApplyRangeAttribute(JsonSchema schema, IEnumerable parentAttributes) + private static void ApplyRangeAttribute(JsonSchema schema, IEnumerable parentAttributes) { dynamic? rangeAttribute = parentAttributes.FirstAssignableToTypeNameOrDefault("System.ComponentModel.DataAnnotations.RangeAttribute"); if (rangeAttribute != null) @@ -1340,7 +1348,7 @@ private void ApplyRangeAttribute(JsonSchema schema, IEnumerable paren } } - private void ApplyTypeExtensionDataAttributes(JsonSchema schema, ContextualType contextualType) + private static void ApplyTypeExtensionDataAttributes(JsonSchema schema, ContextualType contextualType) { var extensionAttributes = contextualType .GetAttributes(true) @@ -1349,7 +1357,7 @@ private void ApplyTypeExtensionDataAttributes(JsonSchema schema, ContextualType ApplyTypeExtensionDataAttributes(schema, extensionAttributes); } - private void ApplyPropertyExtensionDataAttributes(JsonSchemaProperty propertySchema, ContextualAccessorInfo accessorInfo) + private static void ApplyPropertyExtensionDataAttributes(JsonSchemaProperty propertySchema, ContextualAccessorInfo accessorInfo) { var extensionAttributes = accessorInfo .GetAttributes(true) @@ -1360,7 +1368,7 @@ private void ApplyPropertyExtensionDataAttributes(JsonSchemaProperty propertySch private static void ApplyTypeExtensionDataAttributes(JsonSchema schema, IJsonSchemaExtensionDataAttribute[] extensionAttributes) { - if (extensionAttributes.Any()) + if (extensionAttributes.Length > 0) { var extensionData = new Dictionary(); foreach (var kvp in extensionAttributes diff --git a/src/NJsonSchema/Generation/JsonSchemaGeneratorSettings.cs b/src/NJsonSchema/Generation/JsonSchemaGeneratorSettings.cs index 9d2f1987d..12b270fa2 100644 --- a/src/NJsonSchema/Generation/JsonSchemaGeneratorSettings.cs +++ b/src/NJsonSchema/Generation/JsonSchemaGeneratorSettings.cs @@ -36,7 +36,7 @@ public JsonSchemaGeneratorSettings(IReflectionService reflectionService) TypeNameGenerator = new DefaultTypeNameGenerator(); SchemaNameGenerator = new DefaultSchemaNameGenerator(); - ExcludedTypeNames = new string[0]; + ExcludedTypeNames = Array.Empty(); UseXmlDocumentation = true; ResolveExternalXmlDocumentation = true; @@ -62,7 +62,7 @@ public JsonSchemaGeneratorSettings(IReflectionService reflectionService) public bool GenerateKnownTypes { get; set; } = true; /// Gets or sets a value indicating whether to generate xmlObject representation for definitions (default: false). - public bool GenerateXmlObjects { get; set; } = false; + public bool GenerateXmlObjects { get; set; } /// Gets or sets a value indicating whether to ignore properties with the . public bool IgnoreObsoleteProperties { get; set; } diff --git a/src/NJsonSchema/Generation/ReflectionServiceBase.cs b/src/NJsonSchema/Generation/ReflectionServiceBase.cs index 7800c1707..353914d5b 100644 --- a/src/NJsonSchema/Generation/ReflectionServiceBase.cs +++ b/src/NJsonSchema/Generation/ReflectionServiceBase.cs @@ -296,7 +296,7 @@ protected virtual bool IsBinary(ContextualType contextualType) /// /// The type. /// true or false. - private bool IsIAsyncEnumerableType(ContextualType contextualType) + private static bool IsIAsyncEnumerableType(ContextualType contextualType) { return contextualType.Name == "IAsyncEnumerable`1"; } @@ -337,7 +337,7 @@ protected virtual bool IsDictionaryType(ContextualType contextualType) !contextualType.TypeInfo.BaseType.GetTypeInfo().ImplementedInterfaces.Contains(typeof(IDictionary))); } - private bool HasStringEnumConverter(ContextualType contextualType) + private static bool HasStringEnumConverter(ContextualType contextualType) { dynamic? jsonConverterAttribute = contextualType .GetContextOrTypeAttributes(true)? diff --git a/src/NJsonSchema/Generation/SampleJsonDataGenerator.cs b/src/NJsonSchema/Generation/SampleJsonDataGenerator.cs index e5155c75d..517743142 100644 --- a/src/NJsonSchema/Generation/SampleJsonDataGenerator.cs +++ b/src/NJsonSchema/Generation/SampleJsonDataGenerator.cs @@ -10,6 +10,7 @@ using NJsonSchema.Annotations; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; namespace NJsonSchema.Generation @@ -108,7 +109,7 @@ private JToken Generate(JsonSchema schema, Stack schemaStack) } else if (schema.Type.IsInteger()) { - return HandleIntegerType(schema); + return SampleJsonDataGenerator.HandleIntegerType(schema); } else if (schema.Type.IsNumber()) { @@ -149,19 +150,19 @@ private static JToken HandleNumberType(JsonSchema schema) return JToken.FromObject(0.0); } - private JToken HandleIntegerType(JsonSchema schema) + private static JToken HandleIntegerType(JsonSchema schema) { if (schema.ExclusiveMinimumRaw != null) { - return JToken.FromObject(Convert.ToInt32(schema.ExclusiveMinimumRaw)); + return JToken.FromObject(Convert.ToInt32(schema.ExclusiveMinimumRaw, CultureInfo.InvariantCulture)); } else if (schema.ExclusiveMinimum != null) { - return JToken.FromObject(Convert.ToInt32(schema.ExclusiveMinimum)); + return JToken.FromObject(Convert.ToInt32(schema.ExclusiveMinimum, CultureInfo.InvariantCulture)); } else if (schema.Minimum.HasValue) { - return Convert.ToInt32(schema.Minimum); + return Convert.ToInt32(schema.Minimum, CultureInfo.InvariantCulture); } return JToken.FromObject(0); } @@ -170,7 +171,7 @@ private static JToken HandleStringType(JsonSchema schema, JsonSchemaProperty? pr { if (schema.Format == JsonFormatStrings.Date) { - return JToken.FromObject(DateTimeOffset.UtcNow.ToString("yyyy-MM-dd")); + return JToken.FromObject(DateTimeOffset.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)); } else if (schema.Format == JsonFormatStrings.DateTime) { diff --git a/src/NJsonSchema/Generation/SystemTextJsonReflectionService.cs b/src/NJsonSchema/Generation/SystemTextJsonReflectionService.cs index b8dee551e..0704e2cf7 100644 --- a/src/NJsonSchema/Generation/SystemTextJsonReflectionService.cs +++ b/src/NJsonSchema/Generation/SystemTextJsonReflectionService.cs @@ -32,8 +32,8 @@ public override void GenerateProperties(JsonSchema schema, ContextualType contex } if (accessorInfo.MemberInfo is PropertyInfo propertyInfo && - (propertyInfo.GetMethod?.IsPrivate == true || propertyInfo.GetMethod?.IsStatic == true) && - (propertyInfo.SetMethod?.IsPrivate == true || propertyInfo.SetMethod?.IsStatic == true) && + (propertyInfo.GetMethod == null || propertyInfo.GetMethod.IsPrivate == true || propertyInfo.GetMethod.IsStatic == true) && + (propertyInfo.SetMethod == null || propertyInfo.SetMethod.IsPrivate == true || propertyInfo.SetMethod.IsStatic == true) && !propertyInfo.IsDefined(typeof(DataMemberAttribute))) { continue; @@ -45,6 +45,12 @@ public override void GenerateProperties(JsonSchema schema, ContextualType contex continue; } + if (accessorInfo.MemberInfo is PropertyInfo propInfo && + propInfo.GetIndexParameters().Length > 0) + { + continue; + } + var propertyIgnored = false; var jsonIgnoreAttribute = accessorInfo .GetAttributes(true) diff --git a/src/NJsonSchema/Generation/TypeMappers/PrimitiveTypeMapper.cs b/src/NJsonSchema/Generation/TypeMappers/PrimitiveTypeMapper.cs index 3e2f14aaa..ddecf830a 100644 --- a/src/NJsonSchema/Generation/TypeMappers/PrimitiveTypeMapper.cs +++ b/src/NJsonSchema/Generation/TypeMappers/PrimitiveTypeMapper.cs @@ -29,7 +29,7 @@ public PrimitiveTypeMapper(Type mappedType, Action transformer) public Type MappedType { get; } /// Gets a value indicating whether to use a JSON Schema reference for the type. - public bool UseReference { get; } = false; + public bool UseReference { get; } /// Gets the schema for the mapped type. /// The schema. diff --git a/src/NJsonSchema/Infrastructure/Polyfills.cs b/src/NJsonSchema/Infrastructure/Polyfills.cs new file mode 100644 index 000000000..88ded8dcc --- /dev/null +++ b/src/NJsonSchema/Infrastructure/Polyfills.cs @@ -0,0 +1,23 @@ +using System; + +// ReSharper disable once CheckNamespace +namespace NJsonSchema; + +internal static class Polyfills +{ +#if NETFRAMEWORK || NETSTANDARD2_0 + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal static bool Contains(this string source, char c) => source.IndexOf(c) != -1; + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal static bool StartsWith(this string source, char c) => source.Length > 0 && source[0] == c; + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal static bool EndsWith(this string s, char c) => s.Length > 0 && s[^1] == c; +#endif + +#if NETFRAMEWORK + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + internal static bool Contains(this ReadOnlySpan source, string c) => source.IndexOf(c) != -1; +#endif +} \ No newline at end of file diff --git a/src/NJsonSchema/Infrastructure/PropertyRenameAndIgnoreSerializerContractResolver.cs b/src/NJsonSchema/Infrastructure/PropertyRenameAndIgnoreSerializerContractResolver.cs index b6892dea8..405f630a7 100644 --- a/src/NJsonSchema/Infrastructure/PropertyRenameAndIgnoreSerializerContractResolver.cs +++ b/src/NJsonSchema/Infrastructure/PropertyRenameAndIgnoreSerializerContractResolver.cs @@ -32,14 +32,15 @@ public PropertyRenameAndIgnoreSerializerContractResolver() /// One or more JSON properties to ignore. public void IgnoreProperty(Type type, params string[] jsonPropertyNames) { - if (!_ignores.ContainsKey(type.FullName!)) + if (!_ignores.TryGetValue(type.FullName!, out HashSet? value)) { - _ignores[type.FullName!] = new HashSet(); + value = new HashSet(); + _ignores[type.FullName!] = value; } foreach (var prop in jsonPropertyNames) { - _ignores[type.FullName!].Add(prop); + value.Add(prop); } } @@ -49,12 +50,13 @@ public void IgnoreProperty(Type type, params string[] jsonPropertyNames) /// The new JSON property name. public void RenameProperty(Type type, string propertyName, string newJsonPropertyName) { - if (!_renames.ContainsKey(type.FullName!)) + if (!_renames.TryGetValue(type.FullName!, out Dictionary? value)) { - _renames[type.FullName!] = new Dictionary(); + value = new Dictionary(); + _renames[type.FullName!] = value; } - _renames[type.FullName!][propertyName] = newJsonPropertyName; + value[propertyName] = newJsonPropertyName; } /// Creates a JsonProperty for the given System.Reflection.MemberInfo. @@ -87,12 +89,12 @@ protected override JsonProperty CreateProperty(MemberInfo member, MemberSerializ private bool IsIgnored(Type type, string jsonPropertyName) { - if (!_ignores.ContainsKey(type.FullName!)) + if (!_ignores.TryGetValue(type.FullName!, out HashSet? value)) { return false; } - return _ignores[type.FullName!].Contains(jsonPropertyName); + return value.Contains(jsonPropertyName); } private bool IsRenamed(Type type, string jsonPropertyName, out string? newJsonPropertyName) diff --git a/src/NJsonSchema/JsonExtensionObject.cs b/src/NJsonSchema/JsonExtensionObject.cs index fdfaa286a..7b7d2cb6a 100644 --- a/src/NJsonSchema/JsonExtensionObject.cs +++ b/src/NJsonSchema/JsonExtensionObject.cs @@ -36,7 +36,7 @@ internal sealed class ExtensionDataDeserializationConverter : JsonConverter { var obj = (IJsonExtensionObject)Activator.CreateInstance(objectType)!; serializer.Populate(reader, obj); - DeserializeExtensionDataSchemas(obj, serializer); + ExtensionDataDeserializationConverter.DeserializeExtensionDataSchemas(obj, serializer); return obj; } else @@ -59,7 +59,7 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer /// Transforms the extension data so that contained schemas are correctly deserialized. /// The extension object. /// The serializer. - internal void DeserializeExtensionDataSchemas(IJsonExtensionObject extensionObject, JsonSerializer serializer) + internal static void DeserializeExtensionDataSchemas(IJsonExtensionObject extensionObject, JsonSerializer serializer) { if (extensionObject.ExtensionData != null) { @@ -70,7 +70,7 @@ internal void DeserializeExtensionDataSchemas(IJsonExtensionObject extensionObje } } - private object? TryDeserializeValueSchemas(object? value, JsonSerializer serializer) + private static object? TryDeserializeValueSchemas(object? value, JsonSerializer serializer) { if (value is JObject obj) { diff --git a/src/NJsonSchema/JsonReferenceResolver.cs b/src/NJsonSchema/JsonReferenceResolver.cs index ea2c17934..b149fb9ea 100644 --- a/src/NJsonSchema/JsonReferenceResolver.cs +++ b/src/NJsonSchema/JsonReferenceResolver.cs @@ -133,11 +133,11 @@ private async Task ResolveReferenceAsync(object rootObject, stri throw new InvalidOperationException("Could not resolve the JSON path '#' because the root object is not a JsonSchema4."); } - else if (jsonPath.StartsWith("#/")) + else if (jsonPath.StartsWith("#/", StringComparison.Ordinal)) { return ResolveDocumentReference(rootObject, jsonPath, targetType, contractResolver); } - else if (jsonPath.StartsWith("http://") || jsonPath.StartsWith("https://")) + else if (jsonPath.StartsWith("http://", StringComparison.Ordinal) || jsonPath.StartsWith("https://", StringComparison.Ordinal)) { return await ResolveUrlReferenceWithAlreadyResolvedCheckAsync(jsonPath, jsonPath, targetType, contractResolver, append, cancellationToken).ConfigureAwait(false); } @@ -148,7 +148,7 @@ private async Task ResolveReferenceAsync(object rootObject, stri var documentPath = documentPathProvider?.DocumentPath; if (documentPath != null) { - if (documentPath.StartsWith("http://") || documentPath.StartsWith("https://")) + if (documentPath.StartsWith("http://", StringComparison.Ordinal) || documentPath.StartsWith("https://", StringComparison.Ordinal)) { var url = new Uri(new Uri(documentPath), jsonPath).ToString(); return await ResolveUrlReferenceWithAlreadyResolvedCheckAsync(url, jsonPath, targetType, contractResolver, append, cancellationToken).ConfigureAwait(false); @@ -187,15 +187,15 @@ private async Task ResolveFileReferenceWithAlreadyResolvedCheckA fullPath = DynamicApis.HandleSubdirectoryRelativeReferences(fullPath, jsonPath); - if (!_resolvedObjects.ContainsKey(fullPath)) + if (!_resolvedObjects.TryGetValue(fullPath, out IJsonReference? value)) { - var loadedFile = await ResolveFileReferenceAsync(fullPath).ConfigureAwait(false); - loadedFile.DocumentPath = arr[0]; - _resolvedObjects[fullPath] = loadedFile; + value = await ResolveFileReferenceAsync(fullPath, cancellationToken).ConfigureAwait(false); + value.DocumentPath = arr[0]; + _resolvedObjects[fullPath] = value; } - var referencedFile = _resolvedObjects[fullPath]; - var resolvedSchema = arr.Length == 1 ? referencedFile : await ResolveReferenceAsync(referencedFile, arr[1], targetType, contractResolver).ConfigureAwait(false); + var referencedFile = value; + var resolvedSchema = arr.Length == 1 ? referencedFile : await ResolveReferenceAsync(referencedFile, arr[1], targetType, contractResolver, cancellationToken).ConfigureAwait(false); if (resolvedSchema is JsonSchema && append && (_schemaAppender.RootObject as JsonSchema)?.Definitions.Values.Contains(referencedFile) != true) { @@ -216,7 +216,7 @@ private async Task ResolveUrlReferenceWithAlreadyResolvedCheckAs try { var arr = fullJsonPath.Split('#'); - if (!_resolvedObjects.ContainsKey(arr[0])) + if (!_resolvedObjects.TryGetValue(arr[0], out IJsonReference? value)) { var schema = await ResolveUrlReferenceAsync(arr[0], cancellationToken).ConfigureAwait(false); schema.DocumentPath = arr[0]; @@ -225,10 +225,11 @@ private async Task ResolveUrlReferenceWithAlreadyResolvedCheckAs _schemaAppender.AppendSchema((JsonSchema)schema, null); } - _resolvedObjects[arr[0]] = schema; + value = schema; + _resolvedObjects[arr[0]] = value; } - var result = _resolvedObjects[arr[0]]; + var result = value; return arr.Length == 1 ? result : await ResolveReferenceAsync(result, "#" + arr[1], targetType, contractResolver, cancellationToken).ConfigureAwait(false); } catch (Exception exception) diff --git a/src/NJsonSchema/JsonSchema.Serialization.cs b/src/NJsonSchema/JsonSchema.Serialization.cs index 797bc9cbf..ebdae8757 100644 --- a/src/NJsonSchema/JsonSchema.Serialization.cs +++ b/src/NJsonSchema/JsonSchema.Serialization.cs @@ -11,6 +11,7 @@ using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq; using System.Runtime.Serialization; using Namotion.Reflection; @@ -154,7 +155,7 @@ internal object? ExclusiveMaximumRaw } else if (value != null) { - ExclusiveMaximum = Convert.ToDecimal(value); + ExclusiveMaximum = Convert.ToDecimal(value, CultureInfo.InvariantCulture); } } } @@ -176,7 +177,7 @@ internal object? ExclusiveMinimumRaw } else if (value != null) { - ExclusiveMinimum = Convert.ToDecimal(value); + ExclusiveMinimum = Convert.ToDecimal(value, CultureInfo.InvariantCulture); } } } diff --git a/src/NJsonSchema/JsonSchema.cs b/src/NJsonSchema/JsonSchema.cs index 99d493d48..6a8b718eb 100644 --- a/src/NJsonSchema/JsonSchema.cs +++ b/src/NJsonSchema/JsonSchema.cs @@ -51,10 +51,10 @@ public partial class JsonSchema : IDocumentPathProvider internal ObservableCollection _items; private bool _allowAdditionalItems = true; - private JsonSchema? _additionalItemsSchema = null; + private JsonSchema? _additionalItemsSchema; private bool _allowAdditionalProperties = true; - private JsonSchema? _additionalPropertiesSchema = null; + private JsonSchema? _additionalPropertiesSchema; /// Initializes a new instance of the class. public JsonSchema() @@ -812,7 +812,7 @@ public JsonSchema? AdditionalPropertiesSchema /// Gets a value indicating whether the schema represents an tuple type (an array where each item may have a different type). [JsonIgnore] - public bool IsTuple => Type.IsArray() && Items?.Any() == true; + public bool IsTuple => Type.IsArray() && Items?.Count > 0; /// Gets a value indicating whether the schema represents a dictionary type (no properties and AdditionalProperties or PatternProperties contain a schema). [JsonIgnore] diff --git a/src/NJsonSchema/JsonSchemaReferenceUtilities.cs b/src/NJsonSchema/JsonSchemaReferenceUtilities.cs index c29941498..bc0698488 100644 --- a/src/NJsonSchema/JsonSchemaReferenceUtilities.cs +++ b/src/NJsonSchema/JsonSchemaReferenceUtilities.cs @@ -100,11 +100,11 @@ protected override async Task VisitJsonReferenceAsync(IJsonRefer { if (_replaceRefsRound) { - if (path.EndsWith("/definitions/" + typeNameHint) || path.EndsWith("/schemas/" + typeNameHint)) + if (path.EndsWith("/definitions/" + typeNameHint, StringComparison.Ordinal) || path.EndsWith("/schemas/" + typeNameHint, StringComparison.Ordinal)) { // inline $refs in "definitions" return await _referenceResolver - .ResolveReferenceWithoutAppendAsync(_rootObject, reference.ReferencePath, reference.GetType(), _contractResolver) + .ResolveReferenceWithoutAppendAsync(_rootObject, reference.ReferencePath, reference.GetType(), _contractResolver, cancellationToken) .ConfigureAwait(false); } } @@ -112,7 +112,7 @@ protected override async Task VisitJsonReferenceAsync(IJsonRefer { // load $refs and add them to "definitions" reference.Reference = await _referenceResolver - .ResolveReferenceAsync(_rootObject, reference.ReferencePath, reference.GetType(), _contractResolver) + .ResolveReferenceAsync(_rootObject, reference.ReferencePath, reference.GetType(), _contractResolver, cancellationToken) .ConfigureAwait(false); } } diff --git a/src/NJsonSchema/SampleJsonSchemaGenerator.cs b/src/NJsonSchema/SampleJsonSchemaGenerator.cs index 9abf21851..5b480590f 100644 --- a/src/NJsonSchema/SampleJsonSchemaGenerator.cs +++ b/src/NJsonSchema/SampleJsonSchemaGenerator.cs @@ -202,7 +202,7 @@ private void GenerateArray(JToken token, JsonSchema schema, JsonSchema rootSchem } } - private void MergeAndAssignItemSchemas(JsonSchema rootSchema, JsonSchema schema, List itemSchemas, string typeNameHint) + private static void MergeAndAssignItemSchemas(JsonSchema rootSchema, JsonSchema schema, List itemSchemas, string typeNameHint) { var firstItemSchema = itemSchemas.First(); var itemSchema = new JsonSchema @@ -222,7 +222,7 @@ private void MergeAndAssignItemSchemas(JsonSchema rootSchema, JsonSchema schema, schema.Item = new JsonSchema { Reference = itemSchema }; } - private void AddSchemaDefinition(JsonSchema rootSchema, JsonSchema schema, string typeNameHint) + private static void AddSchemaDefinition(JsonSchema rootSchema, JsonSchema schema, string typeNameHint) { if (string.IsNullOrEmpty(typeNameHint) || rootSchema.Definitions.ContainsKey(typeNameHint)) { diff --git a/src/NJsonSchema/Validation/ChildSchemaValidationError.cs b/src/NJsonSchema/Validation/ChildSchemaValidationError.cs index dc9e54b8d..d661ab2ef 100644 --- a/src/NJsonSchema/Validation/ChildSchemaValidationError.cs +++ b/src/NJsonSchema/Validation/ChildSchemaValidationError.cs @@ -35,13 +35,13 @@ public ChildSchemaValidationError(ValidationErrorKind kind, string? property, st /// 2 public override string ToString() { - var output = string.Format("{0}: {1}\n", Kind, Path); + var output = $"{Kind}: {Path}\n"; foreach (var error in Errors) { output += "{\n"; foreach (var validationError in error.Value) { - output += string.Format(" {0}\n", validationError.ToString().Replace("\n", "\n ")); + output += $" {validationError.ToString().Replace("\n", "\n ")}\n"; } output += "}\n"; } diff --git a/src/NJsonSchema/Validation/JsonSchemaValidator.cs b/src/NJsonSchema/Validation/JsonSchemaValidator.cs index 2e6963011..1eef4442b 100644 --- a/src/NJsonSchema/Validation/JsonSchemaValidator.cs +++ b/src/NJsonSchema/Validation/JsonSchemaValidator.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text.RegularExpressions; @@ -20,7 +21,7 @@ namespace NJsonSchema.Validation /// Class to validate a JSON schema against a given . public class JsonSchemaValidator { - private readonly IDictionary _formatValidatorsMap; + private readonly Dictionary _formatValidatorsMap; private readonly JsonSchemaValidatorSettings _settings; /// @@ -85,7 +86,7 @@ protected virtual ICollection Validate(JToken token, JsonSchema ValidateOneOf(token, schema, propertyName, propertyPath, errors); ValidateNot(token, schema, propertyName, propertyPath, errors); ValidateType(token, schema, schemaType, propertyName, propertyPath, errors); - ValidateEnum(token, schema, schemaType, propertyName, propertyPath, errors); + JsonSchemaValidator.ValidateEnum(token, schema, schemaType, propertyName, propertyPath, errors); ValidateProperties(token, schema, schemaType, propertyName, propertyPath, errors); return errors; @@ -106,7 +107,7 @@ private void ValidateType(JToken token, JsonSchema schema, SchemaType schemaType ValidateArray(token, schema, schemaType, type.Key, propertyName, propertyPath, (List)type.Value); ValidateString(token, schema, type.Key, propertyName, propertyPath, (List)type.Value); ValidateNumber(token, schema, type.Key, propertyName, propertyPath, (List)type.Value); - ValidateInteger(token, schema, type.Key, propertyName, propertyPath, (List)type.Value); + JsonSchemaValidator.ValidateInteger(token, schema, type.Key, propertyName, propertyPath, (List)type.Value); ValidateBoolean(token, schema, type.Key, propertyName, propertyPath, (List)type.Value); ValidateNull(token, schema, type.Key, propertyName, propertyPath, (List)type.Value); ValidateObject(token, schema, type.Key, propertyName, propertyPath, (List)type.Value); @@ -124,7 +125,7 @@ private void ValidateType(JToken token, JsonSchema schema, SchemaType schemaType ValidateArray(token, schema, schemaType, schema.Type, propertyName, propertyPath, errors); ValidateString(token, schema, schema.Type, propertyName, propertyPath, errors); ValidateNumber(token, schema, schema.Type, propertyName, propertyPath, errors); - ValidateInteger(token, schema, schema.Type, propertyName, propertyPath, errors); + JsonSchemaValidator.ValidateInteger(token, schema, schema.Type, propertyName, propertyPath, errors); ValidateBoolean(token, schema, schema.Type, propertyName, propertyPath, errors); ValidateNull(token, schema, schema.Type, propertyName, propertyPath, errors); ValidateObject(token, schema, schema.Type, propertyName, propertyPath, errors); @@ -137,7 +138,7 @@ private void ValidateType(JToken token, JsonSchema schema, SchemaType schemaType .Where(t => t != JsonObjectType.None) .ToList(); - private IEnumerable GetTypes(JsonSchema schema) + private static IEnumerable GetTypes(JsonSchema schema) { return JsonObjectTypes.Where(t => schema.Type.HasFlag(t)); } @@ -186,7 +187,7 @@ private void ValidateNot(JToken token, JsonSchema schema, string? propertyName, } } - private void ValidateNull(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) + private static void ValidateNull(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) { if (type.IsNull() && token != null && token.Type != JTokenType.Null) { @@ -194,7 +195,7 @@ private void ValidateNull(JToken token, JsonSchema schema, JsonObjectType type, } } - private void ValidateEnum(JToken token, JsonSchema schema, SchemaType schemaType, string? propertyName, string propertyPath, List errors) + private static void ValidateEnum(JToken token, JsonSchema schema, SchemaType schemaType, string? propertyName, string propertyPath, List errors) { if (schema.IsNullable(schemaType) && token?.Type == JTokenType.Null) { @@ -215,7 +216,10 @@ private void ValidateString(JToken token, JsonSchema schema, JsonObjectType type if (isString) { - var value = token.Type == JTokenType.Date && token is JValue jValue ? jValue.ToString("yyyy-MM-ddTHH:mm:ssK") : token.Value(); + var value = token.Type == JTokenType.Date && token is JValue jValue + ? jValue.ToString("yyyy-MM-ddTHH:mm:ssK", CultureInfo.InvariantCulture) + : token.Value(); + if (value != null) { if (!string.IsNullOrEmpty(schema.Pattern)) @@ -250,7 +254,7 @@ private void ValidateString(JToken token, JsonSchema schema, JsonObjectType type } } - private void ValidateNumber(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) + private static void ValidateNumber(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) { if (type.IsNumber() && token.Type != JTokenType.Float && token.Type != JTokenType.Integer) { @@ -320,7 +324,7 @@ private void ValidateNumber(JToken token, JsonSchema schema, JsonObjectType type } } - private void ValidateInteger(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) + private static void ValidateInteger(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) { if (type.IsInteger() && token.Type != JTokenType.Integer) { @@ -328,7 +332,7 @@ private void ValidateInteger(JToken token, JsonSchema schema, JsonObjectType typ } } - private void ValidateBoolean(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) + private static void ValidateBoolean(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) { if (type.IsBoolean() && token.Type != JTokenType.Boolean) { @@ -336,7 +340,7 @@ private void ValidateBoolean(JToken token, JsonSchema schema, JsonObjectType typ } } - private void ValidateObject(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) + private static void ValidateObject(JToken token, JsonSchema schema, JsonObjectType type, string? propertyName, string propertyPath, List errors) { if (type.IsObject() && !(token is JObject)) { @@ -398,8 +402,8 @@ private void ValidateProperties(JToken token, JsonSchema schema, SchemaType sche { var properties = obj.Properties().ToList(); - ValidateMaxProperties(token, properties, schema, propertyName, propertyPath, errors); - ValidateMinProperties(token, properties, schema, propertyName, propertyPath, errors); + JsonSchemaValidator.ValidateMaxProperties(token, properties, schema, propertyName, propertyPath, errors); + JsonSchemaValidator.ValidateMinProperties(token, properties, schema, propertyName, propertyPath, errors); var additionalProperties = properties.Where(p => !schemaPropertyKeys.Contains(p.Name)).ToList(); @@ -408,22 +412,22 @@ private void ValidateProperties(JToken token, JsonSchema schema, SchemaType sche } } - private string GetPropertyPath(string propertyPath, string propertyName) + private static string GetPropertyPath(string propertyPath, string propertyName) { return !string.IsNullOrEmpty(propertyPath) ? propertyPath + "." + propertyName : propertyName; } - private void ValidateMaxProperties(JToken token, IList properties, JsonSchema schema, string? propertyName, string propertyPath, List errors) + private static void ValidateMaxProperties(JToken token, IList properties, JsonSchema schema, string? propertyName, string propertyPath, List errors) { - if (schema.MaxProperties > 0 && properties.Count() > schema.MaxProperties) + if (schema.MaxProperties > 0 && properties.Count > schema.MaxProperties) { errors.Add(new ValidationError(ValidationErrorKind.TooManyProperties, propertyName, propertyPath, token, schema)); } } - private void ValidateMinProperties(JToken token, IList properties, JsonSchema schema, string? propertyName, string propertyPath, List errors) + private static void ValidateMinProperties(JToken token, IList properties, JsonSchema schema, string? propertyName, string propertyPath, List errors) { - if (schema.MinProperties > 0 && properties.Count() < schema.MinProperties) + if (schema.MinProperties > 0 && properties.Count < schema.MinProperties) { errors.Add(new ValidationError(ValidationErrorKind.TooFewProperties, propertyName, propertyPath, token, schema)); } @@ -468,7 +472,7 @@ private void ValidateAdditionalProperties(JToken token, List addition } } } - else if (!schema.AllowAdditionalProperties && additionalProperties.Any()) + else if (!schema.AllowAdditionalProperties && additionalProperties.Count > 0) { foreach (var property in additionalProperties) { @@ -501,7 +505,7 @@ private void ValidateArray(JToken token, JsonSchema schema, SchemaType schemaTyp { var item = array[index]; - var propertyIndex = string.Format("[{0}]", index); + var propertyIndex = $"[{index}]"; var itemPath = !string.IsNullOrEmpty(propertyPath) ? propertyPath + propertyIndex : propertyIndex; if (schema.Item != null) @@ -526,7 +530,7 @@ private void ValidateAdditionalItems(JToken item, JsonSchema schema, SchemaType { if (schema.Items.Count > 0) { - var propertyIndex = string.Format("[{0}]", index); + var propertyIndex = $"[{index}]"; if (schema.Items.Count > index) { var error = TryCreateChildSchemaError(item, @@ -571,7 +575,7 @@ private void ValidateAdditionalItems(JToken item, JsonSchema schema, SchemaType return new ChildSchemaValidationError(errorKind, property, path, errorDictionary, token, schema); } - private bool TryGetPropertyWithStringComparer(JObject obj, string propertyName, StringComparer comparer, out JToken? value) + private static bool TryGetPropertyWithStringComparer(JObject obj, string propertyName, StringComparer comparer, out JToken? value) { // This method mimics the behavior of the JObject.TryGetValue(string property, StringComparison comparison, out JToken) // extension method using a StringComparer class instead of StringComparison enum value. diff --git a/src/NJsonSchema/Validation/MultiTypeValidationError.cs b/src/NJsonSchema/Validation/MultiTypeValidationError.cs index d5d6af8d5..90114f07d 100644 --- a/src/NJsonSchema/Validation/MultiTypeValidationError.cs +++ b/src/NJsonSchema/Validation/MultiTypeValidationError.cs @@ -36,13 +36,13 @@ public MultiTypeValidationError(ValidationErrorKind kind, string? property, stri /// 2 public override string ToString() { - var output = string.Format("{0}: {1}\n", Kind, Path); + var output = $"{Kind}: {Path}\n"; foreach (var error in Errors) { output += "{" + error.Key + ":\n"; foreach (var validationError in error.Value) { - output += string.Format(" {0}\n", validationError.ToString().Replace("\n", "\n ")); + output += $" {validationError.ToString().Replace("\n", "\n ")}\n"; } output += "}\n"; } diff --git a/src/NJsonSchema/Validation/ValidationError.cs b/src/NJsonSchema/Validation/ValidationError.cs index c13783b7b..a8561ff5a 100644 --- a/src/NJsonSchema/Validation/ValidationError.cs +++ b/src/NJsonSchema/Validation/ValidationError.cs @@ -25,6 +25,7 @@ public ValidationError(ValidationErrorKind errorKind, string? propertyName, stri Kind = errorKind; Property = propertyName; Path = propertyPath != null ? "#/" + propertyPath : "#"; + Token = token; var lineInfo = token as IJsonLineInfo; HasLineInfo = lineInfo != null && lineInfo.HasLineInfo(); @@ -63,12 +64,15 @@ public ValidationError(ValidationErrorKind errorKind, string? propertyName, stri /// Gets the schema element that contains the validation rule. public JsonSchema Schema { get; private set; } + /// Gets the JToken of the element failed the validation. + public JToken? Token { get; private set; } + /// Returns a string that represents the current object. /// A string that represents the current object. /// 2 public override string ToString() { - return string.Format("{0}: {1}", Kind, Path); + return $"{Kind}: {Path}"; } } }