From 892f91efe67abae9420dd329f66d7cd76575508d Mon Sep 17 00:00:00 2001 From: "codebelt-aicia[bot]" Date: Sat, 18 Jul 2026 01:24:38 +0000 Subject: [PATCH 1/8] V10.1.6/service update --- .../PackageReleaseNotes.txt | 6 ++ .../PackageReleaseNotes.txt | 6 ++ .../PackageReleaseNotes.txt | 6 ++ .../PackageReleaseNotes.txt | 6 ++ CHANGELOG.md | 4 ++ Directory.Packages.props | 60 +++++++++---------- 6 files changed, 58 insertions(+), 30 deletions(-) diff --git a/.nuget/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/PackageReleaseNotes.txt index c33c89d..9c033b1 100644 --- a/.nuget/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 10.1.6 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 10.1.5 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Extensions.AspNetCore.Newtonsoft.Json/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.AspNetCore.Newtonsoft.Json/PackageReleaseNotes.txt index 36346da..b939f1a 100644 --- a/.nuget/Codebelt.Extensions.AspNetCore.Newtonsoft.Json/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.AspNetCore.Newtonsoft.Json/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 10.1.6 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 10.1.5 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Extensions.Newtonsoft.Json.App/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Newtonsoft.Json.App/PackageReleaseNotes.txt index ac08443..81236dd 100644 --- a/.nuget/Codebelt.Extensions.Newtonsoft.Json.App/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Newtonsoft.Json.App/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 10.1.6 +Availability: .NET 10 and .NET 9 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 10.1.5 Availability: .NET 10 and .NET 9 diff --git a/.nuget/Codebelt.Extensions.Newtonsoft.Json/PackageReleaseNotes.txt b/.nuget/Codebelt.Extensions.Newtonsoft.Json/PackageReleaseNotes.txt index debc7cf..b366429 100644 --- a/.nuget/Codebelt.Extensions.Newtonsoft.Json/PackageReleaseNotes.txt +++ b/.nuget/Codebelt.Extensions.Newtonsoft.Json/PackageReleaseNotes.txt @@ -1,3 +1,9 @@ +Version: 10.1.6 +Availability: .NET 10, .NET 9 and .NET Standard 2.0 + +# ALM +- CHANGED Dependencies have been upgraded to the latest compatible versions for all supported target frameworks (TFMs) + Version: 10.1.5 Availability: .NET 10, .NET 9 and .NET Standard 2.0 diff --git a/CHANGELOG.md b/CHANGELOG.md index c236969..afc842e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba > [!NOTE] > Changelog entries prior to version 8.4.0 was migrated from previous versions of Cuemon.Extensions.Newtonsoft.Json, Cuemon.Extensions.AspNetCore.Newtonsoft.Json and Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json. +## [10.1.6] - 2026-07-18 + +This is a service update that focuses on package dependencies. + ## [10.1.5] - 2026-06-30 This is a patch release focused on complete DocFX documentation publishing, agent guidance improvements, package version updates, and CI/CD robustness. diff --git a/Directory.Packages.props b/Directory.Packages.props index 49b206f..7a6090a 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,31 +1,31 @@ - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 0e67633dd551c60bd9f5c992b31a6f5eeaad3070 Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Thu, 23 Jul 2026 01:25:47 +0200 Subject: [PATCH 2/8] =?UTF-8?q?=F0=9F=94=A7=20configure=20source=20project?= =?UTF-8?q?=20analysis=20and=20classification?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scope multi-targeting, packaging, and signing to src projects, and enable recommended analyzers with style enforcement so source builds match current Codebelt quality defaults. --- Directory.Build.props | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index f366c5a..50d5155 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -2,6 +2,9 @@ $(MSBuildProjectName.EndsWith('Tests')) + $(MSBuildProjectName.EndsWith('Benchmarks')) + $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())src')) + $(MSBuildProjectDirectory.ToLower().StartsWith('$(MSBuildThisFileDirectory.ToLower())tooling')) $([MSBuild]::IsOSPlatform('Linux')) $([MSBuild]::IsOSPlatform('OSX')) $([MSBuild]::IsOSPlatform('Windows')) @@ -16,7 +19,7 @@ true - + net10.0;net9.0;netstandard2.0 Copyright © Geekle 2024-2026. All rights reserved. gimlichael @@ -36,7 +39,11 @@ true true $(MSBuildThisFileDirectory)newtonsoft.snk - 7035,CA2260 + latest + Recommended + 7035,CA2260,S6618 + v + true @@ -65,7 +72,7 @@ false true 0 - none + none NU1701,NETSDK1206 false true From cfc62b4746621a86694e9885ae357d1231181c98 Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Thu, 23 Jul 2026 01:26:06 +0200 Subject: [PATCH 3/8] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20upgrade=20Microsoft.NE?= =?UTF-8?q?T.Test.Sdk=20to=2018.8.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep the centrally managed test SDK on the latest stable release used by the solution. --- Directory.Packages.props | 60 ++++++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 7a6090a..65599cb 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -1,31 +1,31 @@ - - - true - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 4cdf7b42feb196e19b0df591affb4e254ca58b29 Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Thu, 23 Jul 2026 01:26:11 +0200 Subject: [PATCH 4/8] =?UTF-8?q?=F0=9F=90=B3=20loosen=20docfx=20nginx=20ver?= =?UTF-8?q?sion=20pin?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the 1.31 alpine tag so the documentation image stays current within the nginx minor line without a patch-level pin. --- .docfx/Dockerfile.docfx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docfx/Dockerfile.docfx b/.docfx/Dockerfile.docfx index 465ff57..f4064e2 100644 --- a/.docfx/Dockerfile.docfx +++ b/.docfx/Dockerfile.docfx @@ -1,4 +1,4 @@ -ARG NGINX_VERSION=1.31.2-alpine +ARG NGINX_VERSION=1.31-alpine FROM --platform=$BUILDPLATFORM nginx:${NGINX_VERSION} AS base RUN rm -rf /usr/share/nginx/html/* From 2a1d5908709c84fd7147894eca90c837e8dc54f2 Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Thu, 23 Jul 2026 01:26:16 +0200 Subject: [PATCH 5/8] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20extract=20ExceptionCon?= =?UTF-8?q?verter=20parse=20helpers?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Break the JSON exception parser into focused helpers to cut cognitive complexity and drop legacy suppressions that only existed for the old monolithic path. --- .../Converters/ExceptionConverter.cs | 84 ++++++++++--------- .../GlobalSuppressions.cs | 13 --- .../Converters/ExceptionConverterTest.cs | 11 ++- 3 files changed, 56 insertions(+), 52 deletions(-) delete mode 100644 src/Codebelt.Extensions.Newtonsoft.Json/GlobalSuppressions.cs diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/Converters/ExceptionConverter.cs b/src/Codebelt.Extensions.Newtonsoft.Json/Converters/ExceptionConverter.cs index f68034d..516a06c 100644 --- a/src/Codebelt.Extensions.Newtonsoft.Json/Converters/ExceptionConverter.cs +++ b/src/Codebelt.Extensions.Newtonsoft.Json/Converters/ExceptionConverter.cs @@ -42,7 +42,7 @@ public ExceptionConverter(bool includeStackTrace = false, bool includeData = fal /// /// Writes the JSON representation of the object. /// - /// The to write to. + /// The to write to. /// The value. /// The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) @@ -53,7 +53,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s /// /// Reads the JSON representation of the object. /// - /// The to read from. + /// The to read from. /// Type of the object. /// The existing value of object being read. /// The calling serializer. @@ -72,53 +72,61 @@ private static Stack> ParseJsonReader(JsonReader reader, T var blueprints = new List(); while (reader.Read()) { - if (reader.Depth != lastDepth && blueprints.Count > 0) + if (ShouldPushBlueprints(reader, lastDepth, blueprints)) { stack.Push(blueprints); blueprints = new List(); } - switch (reader.TokenType) - { - case JsonToken.PropertyName: - string memberName = MapOrDefault(reader.Value!.ToString()!); - if (!reader.Read()) - { - // throw - } - var property = properties.SingleOrDefault(pi => pi.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase)); - if (property != null) - { - if (property.Name == nameof(Exception.InnerException)) - { - blueprints.Add(new MemberArgument(memberName, null)); - } - else - { - blueprints.Add(new MemberArgument(memberName, reader.Value)); - } - } - else - { - if (memberName.Equals("type", StringComparison.OrdinalIgnoreCase)) - { - objectType = Formatter.GetType(reader.Value.ToString()); - properties = objectType.GetProperties(MemberReflection.CreateFlags(o => o.ExcludeStatic = true)).ToList(); - blueprints.Add(new MemberArgument(memberName, objectType)); - } - } - break; - case JsonToken.Comment: - break; - case JsonToken.EndObject: - break; - } + HandleToken(reader, ref objectType, ref properties, blueprints); lastDepth = reader.Depth; } return stack; } + private static bool ShouldPushBlueprints(JsonReader reader, int lastDepth, List blueprints) + { + return reader.Depth != lastDepth && blueprints.Count > 0; + } + + private static void HandleToken(JsonReader reader, ref Type objectType, ref List properties, List blueprints) + { + if (reader.TokenType != JsonToken.PropertyName) { return; } + HandlePropertyName(reader, ref objectType, ref properties, blueprints); + } + + private static void HandlePropertyName(JsonReader reader, ref Type objectType, ref List properties, List blueprints) + { + var memberName = MapOrDefault(reader.Value!.ToString()!); + if (!reader.Read()) { return; } + + var property = properties.SingleOrDefault(pi => pi.Name.Equals(memberName, StringComparison.OrdinalIgnoreCase)); + if (property == null) + { + HandleTypeMember(reader, memberName, ref objectType, ref properties, blueprints); + return; + } + + blueprints.Add(CreateMemberArgument(reader, memberName, property)); + } + + private static void HandleTypeMember(JsonReader reader, string memberName, ref Type objectType, ref List properties, List blueprints) + { + if (!memberName.Equals("type", StringComparison.OrdinalIgnoreCase)) { return; } + + objectType = Formatter.GetType(reader.Value.ToString()); + properties = objectType.GetProperties(MemberReflection.CreateFlags(o => o.ExcludeStatic = true)).ToList(); + blueprints.Add(new MemberArgument(memberName, objectType)); + } + + private static MemberArgument CreateMemberArgument(JsonReader reader, string memberName, PropertyInfo property) + { + return property.Name == nameof(Exception.InnerException) + ? new MemberArgument(memberName, null) + : new MemberArgument(memberName, reader.Value); + } + private static string MapOrDefault(string memberName) { switch (memberName.ToLowerInvariant()) diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/GlobalSuppressions.cs b/src/Codebelt.Extensions.Newtonsoft.Json/GlobalSuppressions.cs deleted file mode 100644 index 44d92d6..0000000 --- a/src/Codebelt.Extensions.Newtonsoft.Json/GlobalSuppressions.cs +++ /dev/null @@ -1,13 +0,0 @@ -// This file is used by Code Analysis to maintain SuppressMessage -// attributes that are applied to this project. -// Project-level suppressions either have no target or are given -// a specific target and scoped to a namespace, type, member, etc. - -using System.Diagnostics.CodeAnalysis; - -[assembly: SuppressMessage("Major Code Smell", "S907:\"goto\" statement should not be used", Justification = "Legacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] -[assembly: SuppressMessage("Critical Code Smell", "S3776:Cognitive Complexity of methods should not be too high", Justification = "Legacy code ;-)", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.JsonReaderExtensions.ToHierarchy(Newtonsoft.Json.JsonReader)~Cuemon.IHierarchy{Cuemon.DataPair}")] -[assembly: SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Left for debugging purposes.", Scope = "member", Target = "~P:Cuemon.Extensions.Newtonsoft.Json.DynamicJsonConverterCore.ObjectType")] -[assembly: SuppressMessage("Major Bug", "S3343:Caller information parameters should come at the end of the parameter list", Justification = "For consistency (and to align with Microsoft recent guard improvements), paramName should always be the 2nd parameter (when applicable) OR paramName and message goes next to each other.", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.ValidatorExtensions.InvalidJsonDocument(Cuemon.Validator,Newtonsoft.Json.JsonReader@,System.String,System.String)")] -[assembly: SuppressMessage("Major Bug", "S3343:Caller information parameters should come at the end of the parameter list", Justification = "For consistency (and to align with Microsoft recent guard improvements), paramName should always be the 2nd parameter (when applicable) OR paramName and message goes next to each other.", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.ValidatorExtensions.InvalidJsonDocument(Cuemon.Validator,System.String,System.String,System.String)")] -[assembly: SuppressMessage("Major Code Smell", "S1172:Unused method parameters should be removed", Justification = "False-positive; value is conditionally used.", Scope = "member", Target = "~M:Cuemon.Extensions.Newtonsoft.Json.Converters.ExceptionConverter.ParseJsonReader(Newtonsoft.Json.JsonReader,System.Type)~System.Collections.Generic.Stack{System.Collections.Generic.IList{Cuemon.Reflection.MemberArgument}}")] diff --git a/test/Codebelt.Extensions.Newtonsoft.Json.Tests/Converters/ExceptionConverterTest.cs b/test/Codebelt.Extensions.Newtonsoft.Json.Tests/Converters/ExceptionConverterTest.cs index 1bbc533..638ca88 100644 --- a/test/Codebelt.Extensions.Newtonsoft.Json.Tests/Converters/ExceptionConverterTest.cs +++ b/test/Codebelt.Extensions.Newtonsoft.Json.Tests/Converters/ExceptionConverterTest.cs @@ -148,7 +148,7 @@ public void WriteJson_ShouldSerializeAggregateException_WithMultipleInnerExcepti [Fact] public void ReadJson_ShouldDeserializeException_RoundTrip() { - var original = new ArgumentException("Round-trip message"); + var original = new ArgumentException("Round-trip message", "paramName"); var sut = new ExceptionConverter(); var json = SerializeException(sut, original); @@ -163,6 +163,10 @@ public void ReadJson_ShouldDeserializeException_RoundTrip() var deserialized = serializer.Deserialize(jr, typeof(ArgumentException)) as Exception; Assert.NotNull(deserialized); + var argumentException = Assert.IsType(deserialized); + Assert.Equal(original.Message, argumentException.Message); + Assert.Equal(original.ParamName, argumentException.ParamName); + Assert.Null(argumentException.InnerException); } [Fact] @@ -184,6 +188,11 @@ public void ReadJson_ShouldDeserializeException_WithInnerException() var deserialized = serializer.Deserialize(jr, typeof(ArgumentException)) as Exception; Assert.NotNull(deserialized); + var argumentException = Assert.IsType(deserialized); + Assert.Equal(original.Message, argumentException.Message); + + var innerException = Assert.IsType(argumentException.InnerException); + Assert.Equal(inner.Message, innerException.Message); } [Fact] From de543989dc2388e4e2423e9e21dbdbd2e94d5ab9 Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Thu, 23 Jul 2026 01:26:25 +0200 Subject: [PATCH 6/8] =?UTF-8?q?=F0=9F=8E=A8=20modernize=20using=20declarat?= =?UTF-8?q?ions=20and=20null=20guards?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer using declarations and ThrowIfNull on supported TFMs, and replace the trailing-asterisk EndsWith check with a direct char compare for analyzer-friendly code. --- .../DynamicContractResolver.cs | 6 +- .../Formatters/NewtonsoftJsonFormatter.cs | 18 +- .../JData.cs | 36 ++-- .../JDataResultExtensions.cs | 2 +- .../JsonConverterCollectionExtensionsTest.cs | 174 +++++++++--------- .../JsonSerializationInputFormatterTest.cs | 44 +++-- .../JsonSerializationOutputFormatterTest.cs | 30 ++- .../ServiceCollectionExtensionsTest.cs | 146 ++++++++------- .../JsonConverterCollectionExtensionsTest.cs | 48 ++--- 9 files changed, 236 insertions(+), 268 deletions(-) diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/DynamicContractResolver.cs b/src/Codebelt.Extensions.Newtonsoft.Json/DynamicContractResolver.cs index 07dcf2d..f0989cb 100644 --- a/src/Codebelt.Extensions.Newtonsoft.Json/DynamicContractResolver.cs +++ b/src/Codebelt.Extensions.Newtonsoft.Json/DynamicContractResolver.cs @@ -68,7 +68,11 @@ internal DynamicCamelCasePropertyNamesContractResolver(Action { var serializer = Options.SynchronizeWithJsonConvert ? JsonSerializer.CreateDefault() : JsonSerializer.Create(Options.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - jsonWriter.Formatting = serializer.Formatting; - serializer.Serialize(jsonWriter, source, objectType); - } + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + jsonWriter.Formatting = serializer.Formatting; + serializer.Serialize(jsonWriter, source, objectType); }); } @@ -74,11 +72,9 @@ public override object Deserialize(Stream value, Type objectType) Validator.ThrowIfNull(objectType); var serializer = Options.SynchronizeWithJsonConvert ? JsonSerializer.CreateDefault() : JsonSerializer.Create(Options.Settings); var sr = new StreamReader(value, true); - using (var reader = new JsonTextReader(sr)) - { - reader.CloseInput = false; - return serializer.Deserialize(reader, objectType); - } + using var reader = new JsonTextReader(sr); + reader.CloseInput = false; + return serializer.Deserialize(reader, objectType); } } } diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/JData.cs b/src/Codebelt.Extensions.Newtonsoft.Json/JData.cs index 66eea05..7b2eef4 100644 --- a/src/Codebelt.Extensions.Newtonsoft.Json/JData.cs +++ b/src/Codebelt.Extensions.Newtonsoft.Json/JData.cs @@ -14,47 +14,39 @@ namespace Codebelt.Extensions.Newtonsoft.Json public class JData { /// - /// Creates a sequence of from the specified . + /// Creates a sequence of from the specified . /// /// A that represents a JSON data structure. /// The which may be configured. - /// An sequence from the specified . + /// An sequence from the specified . public static IEnumerable ReadAll(Stream json, Action setup = null) { Validator.ThrowIfNull(json); var options = Patterns.Configure(setup); - using (var sr = new StreamReader(json, options.Encoding, false, options.BufferSize, options.LeaveOpen)) - { - using (var jr = new JsonTextReader(sr)) - { - jr.CloseInput = !options.LeaveOpen; - return ReadAll(jr); - } - } + using var sr = new StreamReader(json, options.Encoding, false, options.BufferSize, options.LeaveOpen); + using var jr = new JsonTextReader(sr); + jr.CloseInput = !options.LeaveOpen; + return ReadAll(jr); } /// - /// Creates a sequence of from the specified . + /// Creates a sequence of from the specified . /// /// A that represents a JSON data structure. - /// An sequence from the specified . + /// An sequence from the specified . public static IEnumerable ReadAll(string json) { Validator.ThrowIfNullOrWhitespace(json); - using (var sr = new StringReader(json)) - { - using (var jr = new JsonTextReader(sr)) - { - return ReadAll(jr); - } - } + using var sr = new StringReader(json); + using var jr = new JsonTextReader(sr); + return ReadAll(jr); } /// - /// Creates a sequence of from the specified . + /// Creates a sequence of from the specified . /// - /// The to parse and extract an sequence from. - /// An sequence from the specified . + /// The to parse and extract an sequence from. + /// An sequence from the specified . public static IEnumerable ReadAll(JsonReader reader) { Validator.ThrowIfNull(reader); diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/JDataResultExtensions.cs b/src/Codebelt.Extensions.Newtonsoft.Json/JDataResultExtensions.cs index b24c1c4..506a4f5 100644 --- a/src/Codebelt.Extensions.Newtonsoft.Json/JDataResultExtensions.cs +++ b/src/Codebelt.Extensions.Newtonsoft.Json/JDataResultExtensions.cs @@ -89,7 +89,7 @@ public static void ExtractArrayValues(this IEnumerable source, stri private static bool HasMatchWithAsterisk(string s, string path) { - return s.EndsWith("*", StringComparison.Ordinal) && + return s.Length > 0 && s[s.Length - 1] == '*' && path.Contains('.') && s.Remove(s.LastIndexOf('.')).Equals(path.Remove(path.LastIndexOf('.')), StringComparison.Ordinal); } diff --git a/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/Converters/JsonConverterCollectionExtensionsTest.cs b/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/Converters/JsonConverterCollectionExtensionsTest.cs index 9aeeec5..7ebdc4c 100644 --- a/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/Converters/JsonConverterCollectionExtensionsTest.cs +++ b/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/Converters/JsonConverterCollectionExtensionsTest.cs @@ -39,104 +39,100 @@ public void AddHttpExceptionDescriptorConverter_ShouldAddHttpExceptionDescriptor oome = e; } - using (var middleware = WebHostTestFactory.Create(hostFixture: null)) + using var middleware = WebHostTestFactory.Create(hostFixture: null); + var context = middleware.Host.Services.GetRequiredService().HttpContext; + var correlationId = Guid.NewGuid().ToString("N"); + var requestId = Guid.NewGuid().ToString("N"); + + var sut1 = new HttpExceptionDescriptor(oome, message: "Custom non-revealing message.") { - var context = middleware.Host.Services.GetRequiredService().HttpContext; - var correlationId = Guid.NewGuid().ToString("N"); - var requestId = Guid.NewGuid().ToString("N"); + CorrelationId = correlationId, + RequestId = requestId, + HelpLink = new Uri("https://docs.microsoft.com/en-us/dotnet/api/system.outofmemoryexception") + }; + + sut1.AddEvidence("Request", context.Request, request => new HttpRequestEvidence(request)); - var sut1 = new HttpExceptionDescriptor(oome, message: "Custom non-revealing message.") + // TODO: look into IncludeException* and Include equivalents + + var sut2 = new NewtonsoftJsonFormatterOptions() + { + SensitivityDetails = sensitivityDetails + }; + + var dc = sut2.Settings.Converters.SingleOrDefault(jc => jc.CanConvert(typeof(HttpExceptionDescriptor))); + if (dc != null) { sut2.Settings.Converters.Remove(dc); } + sut2.Settings.Converters.AddHttpExceptionDescriptorConverter(o => + { + o.SensitivityDetails = sensitivityDetails; + }); + + Assert.Collection(sut2.Settings.Converters.Where(jc => jc.CanConvert(typeof(HttpExceptionDescriptor))), jc => + { + var result = StreamFactory.Create(writer => { - CorrelationId = correlationId, - RequestId = requestId, - HelpLink = new Uri("https://docs.microsoft.com/en-us/dotnet/api/system.outofmemoryexception") - }; + var js = JsonSerializer.Create(sut2.Settings); + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + js.Serialize(jsonWriter, sut1); + }); - sut1.AddEvidence("Request", context.Request, request => new HttpRequestEvidence(request)); + var json = result.ToEncodedString(); + + Assert.True(jc.CanWrite); + Assert.False(jc.CanRead); + Assert.True(jc.CanConvert(typeof(HttpExceptionDescriptor))); - // TODO: look into IncludeException* and Include equivalents + Assert.Contains("\"error\":", json); + Assert.Contains("\"status\": 500", json); + Assert.Contains("\"code\": \"InternalServerError\"", json); + Assert.Contains("\"message\": \"Custom non-revealing message.\"", json); + Assert.Contains("\"helpLink\": \"https://docs.microsoft.com/en-us/dotnet/api/system.outofmemoryexception\"", json); - var sut2 = new NewtonsoftJsonFormatterOptions() + Assert.Contains($"\"correlationId\": \"{correlationId}\"", json); + Assert.Contains($"\"requestId\": \"{requestId}\"", json); + + Condition.FlipFlop(sensitivityDetails.HasFlag(FaultSensitivityDetails.Failure), () => + { + Assert.Contains("\"failure\":", json); + Assert.Contains("\"type\": \"System.OutOfMemoryException\"", json); + Assert.Contains("\"source\": \"Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests\"", json); + Assert.Contains("\"message\": \"Insufficient memory to continue the execution of the program.\"", json); + }, () => { - SensitivityDetails = sensitivityDetails - }; + Assert.DoesNotContain("\"failure\":", json); + Assert.DoesNotContain("\"type\": \"System.OutOfMemoryException\"", json); + Assert.DoesNotContain("\"source\": \"Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests\"", json); + Assert.DoesNotContain("\"message\": \"Insufficient memory to continue the execution of the program.\"", json); + }); - var dc = sut2.Settings.Converters.SingleOrDefault(jc => jc.CanConvert(typeof(HttpExceptionDescriptor))); - if (dc != null) { sut2.Settings.Converters.Remove(dc); } - sut2.Settings.Converters.AddHttpExceptionDescriptorConverter(o => + Condition.FlipFlop(sensitivityDetails.HasFlag(FaultSensitivityDetails.StackTrace), () => { - o.SensitivityDetails = sensitivityDetails; + Assert.Contains("\"stack\":", json); + Assert.Contains("\"at Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.JsonConverterCollectionExtensionsTest.AddHttpExceptionDescriptorConverter_ShouldAddHttpExceptionDescriptorToConverterCollection", json); + }, () => + { + Assert.DoesNotContain("\"stack\":", json); + Assert.DoesNotContain("\"at Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.JsonConverterCollectionExtensionsTest.AddHttpExceptionDescriptorConverter_ShouldAddHttpExceptionDescriptorToConverterCollection", json); }); - Assert.Collection(sut2.Settings.Converters.Where(jc => jc.CanConvert(typeof(HttpExceptionDescriptor))), jc => + Condition.FlipFlop(sensitivityDetails.HasFlag(FaultSensitivityDetails.Evidence), () => + { + Assert.Contains("\"evidence\":", json); + Assert.Contains("\"request\":", json); + Assert.Contains("\"location\": \"http:///\"", json); + Assert.Contains("\"method\": \"GET\"", json); + Assert.Contains("\"headers\":", json); + Assert.Contains("\"query\":", json); + Assert.Contains("\"cookies\":", json); + Assert.Contains("\"body\":", json); + }, () => { - var result = StreamFactory.Create(writer => - { - var js = JsonSerializer.Create(sut2.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - js.Serialize(jsonWriter, sut1); - } - }); - - var json = result.ToEncodedString(); - - Assert.True(jc.CanWrite); - Assert.False(jc.CanRead); - Assert.True(jc.CanConvert(typeof(HttpExceptionDescriptor))); - - Assert.Contains("\"error\":", json); - Assert.Contains("\"status\": 500", json); - Assert.Contains("\"code\": \"InternalServerError\"", json); - Assert.Contains("\"message\": \"Custom non-revealing message.\"", json); - Assert.Contains("\"helpLink\": \"https://docs.microsoft.com/en-us/dotnet/api/system.outofmemoryexception\"", json); - - Assert.Contains($"\"correlationId\": \"{correlationId}\"", json); - Assert.Contains($"\"requestId\": \"{requestId}\"", json); - - Condition.FlipFlop(sensitivityDetails.HasFlag(FaultSensitivityDetails.Failure), () => - { - Assert.Contains("\"failure\":", json); - Assert.Contains("\"type\": \"System.OutOfMemoryException\"", json); - Assert.Contains("\"source\": \"Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests\"", json); - Assert.Contains("\"message\": \"Insufficient memory to continue the execution of the program.\"", json); - }, () => - { - Assert.DoesNotContain("\"failure\":", json); - Assert.DoesNotContain("\"type\": \"System.OutOfMemoryException\"", json); - Assert.DoesNotContain("\"source\": \"Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests\"", json); - Assert.DoesNotContain("\"message\": \"Insufficient memory to continue the execution of the program.\"", json); - }); - - Condition.FlipFlop(sensitivityDetails.HasFlag(FaultSensitivityDetails.StackTrace), () => - { - Assert.Contains("\"stack\":", json); - Assert.Contains("\"at Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.JsonConverterCollectionExtensionsTest.AddHttpExceptionDescriptorConverter_ShouldAddHttpExceptionDescriptorToConverterCollection", json); - }, () => - { - Assert.DoesNotContain("\"stack\":", json); - Assert.DoesNotContain("\"at Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Converters.JsonConverterCollectionExtensionsTest.AddHttpExceptionDescriptorConverter_ShouldAddHttpExceptionDescriptorToConverterCollection", json); - }); - - Condition.FlipFlop(sensitivityDetails.HasFlag(FaultSensitivityDetails.Evidence), () => - { - Assert.Contains("\"evidence\":", json); - Assert.Contains("\"request\":", json); - Assert.Contains("\"location\": \"http:///\"", json); - Assert.Contains("\"method\": \"GET\"", json); - Assert.Contains("\"headers\":", json); - Assert.Contains("\"query\":", json); - Assert.Contains("\"cookies\":", json); - Assert.Contains("\"body\":", json); - }, () => - { - Assert.DoesNotContain("\"evidence\":", json); - }); - - TestOutput.WriteLine(json); + Assert.DoesNotContain("\"evidence\":", json); }); - } + + TestOutput.WriteLine(json); + }); } [Fact] @@ -155,11 +151,9 @@ public void AddStringValuesConverter_ShouldAddStringValuesConverterToConverterCo var result = StreamFactory.Create(writer => { var js = JsonSerializer.Create(sut2.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - js.Serialize(jsonWriter, sut1); - } + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + js.Serialize(jsonWriter, sut1); }); var json = result.ToEncodedString(); diff --git a/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/JsonSerializationInputFormatterTest.cs b/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/JsonSerializationInputFormatterTest.cs index 4ad9fb6..ce2f165 100644 --- a/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/JsonSerializationInputFormatterTest.cs +++ b/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/JsonSerializationInputFormatterTest.cs @@ -49,39 +49,37 @@ public void Ctor_VerifyThatApplicationJsonAndTextJson_WasAdded_ToSupportedMediaT [Fact] public async Task ReadRequestBodyAsync_ShouldReturnCreated() { - using (var filter = WebHostTestFactory.Create(services => + using var filter = WebHostTestFactory.Create(services => { services.AddControllers(o => { o.Filters.Add(); }) .AddApplicationPart(typeof(FakeController).Assembly) .AddNewtonsoftJsonFormatters(o => o.Settings.DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK"); // default ISO8601 (ToString("O") }, app => - { - app.UseRouting(); - app.UseEndpoints(routes => { routes.MapControllers(); }); - }, hostFixture: null)) { - var wf = new WeatherForecast(); - var formatter = new NewtonsoftJsonFormatter(o => - { - o.Settings.DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK"; // default ISO8601 (ToString("O") - o.Settings.Formatting = Formatting.Indented; - }); - var stream = formatter.Serialize(wf); - var client = filter.Host.GetTestClient(); + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, hostFixture: null); + var wf = new WeatherForecast(); + var formatter = new NewtonsoftJsonFormatter(o => + { + o.Settings.DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK"; // default ISO8601 (ToString("O") + o.Settings.Formatting = Formatting.Indented; + }); + var stream = formatter.Serialize(wf); + var client = filter.Host.GetTestClient(); - var result = await client.PostAsync("/fake", new StringContent(stream.ToEncodedString(o => o.LeaveOpen = true), Encoding.UTF8, "application/json")); - var model = await result.Content.ReadAsStringAsync(); + var result = await client.PostAsync("/fake", new StringContent(stream.ToEncodedString(o => o.LeaveOpen = true), Encoding.UTF8, "application/json")); + var model = await result.Content.ReadAsStringAsync(); - TestOutput.WriteLine(stream.ToEncodedString(o => o.LeaveOpen = true)); - TestOutput.WriteLine("---"); - TestOutput.WriteLine(model); + TestOutput.WriteLine(stream.ToEncodedString(o => o.LeaveOpen = true)); + TestOutput.WriteLine("---"); + TestOutput.WriteLine(model); - Assert.Equal(stream.ToEncodedString(), model, ignoreLineEndingDifferences: true); + Assert.Equal(stream.ToEncodedString(), model, ignoreLineEndingDifferences: true); - Assert.Equal(StatusCodes.Status201Created, (int)result.StatusCode); - Assert.Equal(HttpMethod.Post, result.RequestMessage.Method); - Assert.Equal(new Uri("http://localhost/fake"), result.RequestMessage.RequestUri); - } + Assert.Equal(StatusCodes.Status201Created, (int)result.StatusCode); + Assert.Equal(HttpMethod.Post, result.RequestMessage.Method); + Assert.Equal(new Uri("http://localhost/fake"), result.RequestMessage.RequestUri); } } } diff --git a/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/JsonSerializationOutputFormatterTest.cs b/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/JsonSerializationOutputFormatterTest.cs index bfe04b2..4b7c71d 100644 --- a/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/JsonSerializationOutputFormatterTest.cs +++ b/test/Codebelt.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json.Tests/JsonSerializationOutputFormatterTest.cs @@ -46,32 +46,30 @@ public void Ctor_VerifyThatApplicationJsonAndTextJson_WasAdded_ToSupportedMediaT [Fact] public async Task WriteResponseBodyAsync_ShouldReturnOk() { - using (var filter = WebHostTestFactory.Create(services => + using var filter = WebHostTestFactory.Create(services => { services.AddControllers(o => { o.Filters.Add(); }) .AddApplicationPart(typeof(FakeController).Assembly) .AddNewtonsoftJsonFormatters(); }, app => - { - app.UseRouting(); - app.UseEndpoints(routes => { routes.MapControllers(); }); - }, hostFixture: null)) { - var client = filter.Host.GetTestClient(); + app.UseRouting(); + app.UseEndpoints(routes => { routes.MapControllers(); }); + }, hostFixture: null); + var client = filter.Host.GetTestClient(); - var result = await client.GetAsync("/fake"); - var model = await result.Content.ReadAsStringAsync(); + var result = await client.GetAsync("/fake"); + var model = await result.Content.ReadAsStringAsync(); - TestOutput.WriteLine(model); + TestOutput.WriteLine(model); - Assert.Contains("\"date\":", model); - Assert.Contains("\"temperatureC\":", model); - Assert.Contains("\"temperatureF\":", model); - Assert.Contains("\"summary\":", model); + Assert.Contains("\"date\":", model); + Assert.Contains("\"temperatureC\":", model); + Assert.Contains("\"temperatureF\":", model); + Assert.Contains("\"summary\":", model); - Assert.Equal(StatusCodes.Status200OK, (int)result.StatusCode); - Assert.Equal(HttpMethod.Get, result.RequestMessage.Method); - } + Assert.Equal(StatusCodes.Status200OK, (int)result.StatusCode); + Assert.Equal(HttpMethod.Get, result.RequestMessage.Method); } } } \ No newline at end of file diff --git a/test/Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Tests/Formatters/ServiceCollectionExtensionsTest.cs b/test/Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Tests/Formatters/ServiceCollectionExtensionsTest.cs index 019925d..6aa003a 100644 --- a/test/Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Tests/Formatters/ServiceCollectionExtensionsTest.cs +++ b/test/Codebelt.Extensions.AspNetCore.Newtonsoft.Json.Tests/Formatters/ServiceCollectionExtensionsTest.cs @@ -222,87 +222,85 @@ public async Task AddNewtonsoftJsonExceptionResponseFormatter_ShouldCaptureExcep [InlineData(FaultSensitivityDetails.None)] public async Task AddNewtonsoftJsonExceptionResponseFormatter_AuthorizationResponseHandler_BasicScheme_ShouldRenderResponseInJsonByNewtonsoft_UsingAspNetBootstrapping(FaultSensitivityDetails sensitivityDetails) { - using (var startup = WebHostTestFactory.Create(services => - { - services.AddNewtonsoftJsonExceptionResponseFormatter(); - services.AddAuthorizationResponseHandler(); - services.AddAuthentication(BasicAuthorizationHeader.Scheme) - .AddBasic(o => - { - o.RequireSecureConnection = false; - o.Authenticator = (username, password) => null; - }); - services.AddAuthorization(o => - { - o.FallbackPolicy = new AuthorizationPolicyBuilder() - .AddAuthenticationSchemes(BasicAuthorizationHeader.Scheme) - .RequireAuthenticatedUser() - .Build(); - - }); - services.AddRouting(); - services.PostConfigureAllExceptionDescriptorOptions(o => o.SensitivityDetails = sensitivityDetails); - }, app => - { - app.UseRouting(); - app.UseAuthentication(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapGet("/", context => context.Response.WriteAsync($"Hello {context.User.Identity!.Name}")); - }); - }, hostFixture: null)) + using var startup = WebHostTestFactory.Create(services => { - var client = startup.Host.GetTestClient(); - var bb = new BasicAuthorizationHeaderBuilder() - .AddUserName("Agent") - .AddPassword("Test"); + services.AddNewtonsoftJsonExceptionResponseFormatter(); + services.AddAuthorizationResponseHandler(); + services.AddAuthentication(BasicAuthorizationHeader.Scheme) + .AddBasic(o => + { + o.RequireSecureConnection = false; + o.Authenticator = (username, password) => null; + }); + services.AddAuthorization(o => + { + o.FallbackPolicy = new AuthorizationPolicyBuilder() + .AddAuthenticationSchemes(BasicAuthorizationHeader.Scheme) + .RequireAuthenticatedUser() + .Build(); + + }); + services.AddRouting(); + services.PostConfigureAllExceptionDescriptorOptions(o => o.SensitivityDetails = sensitivityDetails); + }, app => + { + app.UseRouting(); + app.UseAuthentication(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => + { + endpoints.MapGet("/", context => context.Response.WriteAsync($"Hello {context.User.Identity!.Name}")); + }); + }, hostFixture: null); + var client = startup.Host.GetTestClient(); + var bb = new BasicAuthorizationHeaderBuilder() + .AddUserName("Agent") + .AddPassword("Test"); - client.DefaultRequestHeaders.Add(HeaderNames.Authorization, bb.Build().ToString()); - client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json"); + client.DefaultRequestHeaders.Add(HeaderNames.Authorization, bb.Build().ToString()); + client.DefaultRequestHeaders.Add(HeaderNames.Accept, "application/json"); - var result = await client.GetAsync("/"); - var content = await result.Content.ReadAsStringAsync(); + var result = await client.GetAsync("/"); + var content = await result.Content.ReadAsStringAsync(); - TestOutput.WriteLine(content); + TestOutput.WriteLine(content); - Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode); - Assert.Equal("Basic realm=\"AuthenticationServer\"", result.Headers.WwwAuthenticate.ToString()); - if (sensitivityDetails == FaultSensitivityDetails.All) - { - Assert.Equal(""" - { - "error": { - "status": 401, - "code": "Unauthorized", - "message": "The request has not been applied because it lacks valid authentication credentials for the target resource.", - "failure": { - "type": "Cuemon.AspNetCore.Http.UnauthorizedException", - "message": "The request has not been applied because it lacks valid authentication credentials for the target resource.", - "headers": {}, - "statusCode": 401, - "reasonPhrase": "Unauthorized", - "inner": { - "type": "System.Security.SecurityException", - "message": "Unable to authenticate Agent." - } - } - } - } - """.ReplaceLineEndings(), content.ReplaceLineEndings()); - } - else - { - Assert.Equal(""" - { - "error": { - "status": 401, - "code": "Unauthorized", - "message": "The request has not been applied because it lacks valid authentication credentials for the target resource." + Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode); + Assert.Equal("Basic realm=\"AuthenticationServer\"", result.Headers.WwwAuthenticate.ToString()); + if (sensitivityDetails == FaultSensitivityDetails.All) + { + Assert.Equal(""" + { + "error": { + "status": 401, + "code": "Unauthorized", + "message": "The request has not been applied because it lacks valid authentication credentials for the target resource.", + "failure": { + "type": "Cuemon.AspNetCore.Http.UnauthorizedException", + "message": "The request has not been applied because it lacks valid authentication credentials for the target resource.", + "headers": {}, + "statusCode": 401, + "reasonPhrase": "Unauthorized", + "inner": { + "type": "System.Security.SecurityException", + "message": "Unable to authenticate Agent." } } - """.ReplaceLineEndings(), content.ReplaceLineEndings()); - } + } + } + """.ReplaceLineEndings(), content.ReplaceLineEndings()); + } + else + { + Assert.Equal(""" + { + "error": { + "status": 401, + "code": "Unauthorized", + "message": "The request has not been applied because it lacks valid authentication credentials for the target resource." + } + } + """.ReplaceLineEndings(), content.ReplaceLineEndings()); } } diff --git a/test/Codebelt.Extensions.Newtonsoft.Json.Tests/Converters/JsonConverterCollectionExtensionsTest.cs b/test/Codebelt.Extensions.Newtonsoft.Json.Tests/Converters/JsonConverterCollectionExtensionsTest.cs index 0605250..d771645 100644 --- a/test/Codebelt.Extensions.Newtonsoft.Json.Tests/Converters/JsonConverterCollectionExtensionsTest.cs +++ b/test/Codebelt.Extensions.Newtonsoft.Json.Tests/Converters/JsonConverterCollectionExtensionsTest.cs @@ -32,11 +32,9 @@ public void AddStringEnumConverter_ShouldAddStringEnumConverterToConverterCollec var result = StreamFactory.Create(writer => { var js = JsonSerializer.Create(sut2.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - js.Serialize(jsonWriter, sut1); - } + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + js.Serialize(jsonWriter, sut1); }); var json = result.ToEncodedString(); @@ -64,11 +62,9 @@ public void AddStringFlagsEnumConverter_ShouldAddStringFlagsEnumConverterToConve var result = StreamFactory.Create(writer => { var js = JsonSerializer.Create(sut2.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - js.Serialize(jsonWriter, sut1); - } + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + js.Serialize(jsonWriter, sut1); }); var json = result.ToEncodedString(); @@ -98,11 +94,9 @@ public void AddStringEnumConverter_ShouldAddStringEnumConverterToConverterCollec var result = StreamFactory.Create(writer => { var js = JsonSerializer.Create(sut2.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - js.Serialize(jsonWriter, sut1); - } + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + js.Serialize(jsonWriter, sut1); }); var json = result.ToEncodedString(); @@ -129,11 +123,9 @@ public void AddStringFlagsEnumConverter_ShouldAddStringFlagsEnumConverterToConve var result = StreamFactory.Create(writer => { var js = JsonSerializer.Create(sut2.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - js.Serialize(jsonWriter, sut1); - } + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + js.Serialize(jsonWriter, sut1); }); var json = result.ToEncodedString(); @@ -183,11 +175,9 @@ public void AddExceptionDescriptorConverter_ShouldAddExceptionDescriptorConverte var result = StreamFactory.Create(writer => { var js = JsonSerializer.Create(sut2.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - js.Serialize(jsonWriter, sut1); - } + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + js.Serialize(jsonWriter, sut1); }); var json = result.ToEncodedString(); @@ -251,11 +241,9 @@ public void AddDataPairConverter_ShouldAddDataPairConverterToConverterCollection var result = StreamFactory.Create(writer => { var js = JsonSerializer.Create(sut2.Settings); - using (var jsonWriter = new JsonTextWriter(writer)) - { - jsonWriter.CloseOutput = false; - js.Serialize(jsonWriter, sut1); - } + using var jsonWriter = new JsonTextWriter(writer); + jsonWriter.CloseOutput = false; + js.Serialize(jsonWriter, sut1); }); var json = result.ToEncodedString(); From fa9bbd53d61266eecc409108837b8792e783455e Mon Sep 17 00:00:00 2001 From: "aicia[bot]" Date: Thu, 23 Jul 2026 01:26:31 +0200 Subject: [PATCH 7/8] =?UTF-8?q?=F0=9F=93=9D=20simplify=20XML=20cref=20docu?= =?UTF-8?q?mentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop redundant T: cref prefixes so API docs resolve with the simpler generic type form used elsewhere in the codebase. --- .../JsonConverterCollectionExtensions.cs | 6 +++--- .../JsonConverterCollectionExtensions.cs | 14 +++++++------- .../Converters/StringFlagsEnumConverter.cs | 4 ++-- .../Converters/TransientFaultExceptionConverter.cs | 4 ++-- .../JsonConverterFactory.cs | 12 ++++++------ 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/src/Codebelt.Extensions.AspNetCore.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs b/src/Codebelt.Extensions.AspNetCore.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs index af4658d..c5602ee 100644 --- a/src/Codebelt.Extensions.AspNetCore.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs +++ b/src/Codebelt.Extensions.AspNetCore.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs @@ -20,7 +20,7 @@ public static class JsonConverterCollectionExtensions /// /// Adds a JSON converter to the list. /// - /// The to extend. + /// The to extend. /// A reference to after the operation has completed. public static ICollection AddProblemDetailsConverter(this ICollection converters) { @@ -74,7 +74,7 @@ private static void WriteProblemDetails(JsonWriter writer, ProblemDetails pd, Js /// /// Adds an JSON converter to the list. /// - /// The to extend. + /// The to extend. /// The which may be configured. /// A reference to after the operation has completed. public static ICollection AddHttpExceptionDescriptorConverter(this ICollection converters, Action setup = null) @@ -112,7 +112,7 @@ public static ICollection AddHttpExceptionDescriptorConverter(thi /// /// Adds an JSON converter to the list. /// - /// The to extend. + /// The to extend. /// A reference to after the operation has completed. public static ICollection AddStringValuesConverter(this ICollection converters) { diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs b/src/Codebelt.Extensions.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs index c8de750..e7a29fb 100644 --- a/src/Codebelt.Extensions.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs +++ b/src/Codebelt.Extensions.Newtonsoft.Json/Converters/JsonConverterCollectionExtensions.cs @@ -18,7 +18,7 @@ public static class JsonConverterCollectionExtensions /// /// Adds an JSON converter to the list. /// - /// The to extend. + /// The to extend. /// The optional to apply. /// A reference to after the operation has completed. public static ICollection AddStringEnumConverter(this ICollection converters, NamingStrategy ns = null) @@ -30,7 +30,7 @@ public static ICollection AddStringEnumConverter(this ICollection /// /// Adds a combined and JSON converter to the list. /// - /// The to extend. + /// The to extend. /// The optional to apply. /// A reference to after the operation has completed. public static ICollection AddStringFlagsEnumConverter(this ICollection converters, NamingStrategy ns = null) @@ -42,7 +42,7 @@ public static ICollection AddStringFlagsEnumConverter(this IColle /// /// Adds an JSON converter to the list. /// - /// The to extend. + /// The to extend. /// The which may be configured. /// The delegate that is invoked just after writing JSON start object (Error). /// The delegate that is invoked just before writing the JSON end object. @@ -91,7 +91,7 @@ public static ICollection AddExceptionDescriptorConverterOf(th /// /// Adds an JSON converter to the list. /// - /// The to extend. + /// The to extend. /// The value that determine whether the stack of an exception is included in the converted result. /// The value that determine whether the data of an exception is included in the converted result. /// A reference to after the operation has completed. @@ -104,7 +104,7 @@ public static ICollection AddExceptionConverter(this ICollection< /// /// Adds a JSON converter to the list. /// - /// The to extend. + /// The to extend. /// A reference to after the operation has completed. public static ICollection AddFailureConverter(this ICollection converters) { @@ -118,7 +118,7 @@ public static ICollection AddFailureConverter(this ICollection /// Adds an JSON converter to the list. /// - /// The to extend. + /// The to extend. /// A reference to after the operation has completed. public static ICollection AddTransientFaultExceptionConverter(this ICollection converters) { @@ -129,7 +129,7 @@ public static ICollection AddTransientFaultExceptionConverter(thi /// /// Adds an JSON converter to the list. /// - /// The to extend. + /// The to extend. /// A reference to after the operation has completed. public static ICollection AddDataPairConverter(this ICollection converters) { diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/Converters/StringFlagsEnumConverter.cs b/src/Codebelt.Extensions.Newtonsoft.Json/Converters/StringFlagsEnumConverter.cs index 48739c8..274d971 100644 --- a/src/Codebelt.Extensions.Newtonsoft.Json/Converters/StringFlagsEnumConverter.cs +++ b/src/Codebelt.Extensions.Newtonsoft.Json/Converters/StringFlagsEnumConverter.cs @@ -30,7 +30,7 @@ public StringFlagsEnumConverter(NamingStrategy namingStrategy) : base(namingStra /// /// Writes the JSON representation of the object. /// - /// The to write to. + /// The to write to. /// The value. /// The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) @@ -72,7 +72,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s /// /// Reads the JSON representation of the object. /// - /// The to read from. + /// The to read from. /// Type of the object. /// The existing value of object being read. /// The calling serializer. diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/Converters/TransientFaultExceptionConverter.cs b/src/Codebelt.Extensions.Newtonsoft.Json/Converters/TransientFaultExceptionConverter.cs index 35c136a..8361bae 100644 --- a/src/Codebelt.Extensions.Newtonsoft.Json/Converters/TransientFaultExceptionConverter.cs +++ b/src/Codebelt.Extensions.Newtonsoft.Json/Converters/TransientFaultExceptionConverter.cs @@ -18,7 +18,7 @@ public class TransientFaultExceptionConverter : JsonConverter /// /// Writes the JSON representation of the object. /// - /// The to write to. + /// The to write to. /// The value to serialize. /// The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) @@ -30,7 +30,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s /// /// Reads the JSON representation of the object. /// - /// The to read from. + /// The to read from. /// Type of the object to deserialize. /// The existing value of object being read. /// The calling serializer. diff --git a/src/Codebelt.Extensions.Newtonsoft.Json/JsonConverterFactory.cs b/src/Codebelt.Extensions.Newtonsoft.Json/JsonConverterFactory.cs index 4011bb0..93d601a 100644 --- a/src/Codebelt.Extensions.Newtonsoft.Json/JsonConverterFactory.cs +++ b/src/Codebelt.Extensions.Newtonsoft.Json/JsonConverterFactory.cs @@ -98,7 +98,7 @@ internal DynamicJsonConverter(Func predicate, Action /// Writes the JSON representation of the object. /// - /// The to write to. + /// The to write to. /// The value. /// The calling serializer. public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) @@ -115,7 +115,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s /// /// Reads the JSON representation of the object. /// - /// The to read from. + /// The to read from. /// Type of the object. /// The existing value of object being read. /// The calling serializer. @@ -137,15 +137,15 @@ public override bool CanConvert(Type objectType) } /// - /// Gets a value indicating whether this can read JSON. + /// Gets a value indicating whether this can read JSON. /// - /// true if this can read JSON; otherwise, false. + /// true if this can read JSON; otherwise, false. public override bool CanRead => Reader != null; /// - /// Gets a value indicating whether this can write JSON. + /// Gets a value indicating whether this can write JSON. /// - /// true if this can write JSON; otherwise, false. + /// true if this can write JSON; otherwise, false. public override bool CanWrite => Writer != null; } } From c0b0f3b5a206492643c4602e76a6470b81fb62d9 Mon Sep 17 00:00:00 2001 From: gimlichael Date: Thu, 23 Jul 2026 01:28:45 +0200 Subject: [PATCH 8/8] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20update=20package=20dep?= =?UTF-8?q?endencies=20and=20enhance=20maintainability=20in=20changelog?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index afc842e..99782c6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,9 +7,29 @@ For more details, please refer to `PackageReleaseNotes.txt` on a per assembly ba > [!NOTE] > Changelog entries prior to version 8.4.0 was migrated from previous versions of Cuemon.Extensions.Newtonsoft.Json, Cuemon.Extensions.AspNetCore.Newtonsoft.Json and Cuemon.Extensions.AspNetCore.Mvc.Formatters.Newtonsoft.Json. -## [10.1.6] - 2026-07-18 +## [10.1.6] - 2026-07-23 -This is a service update that focuses on package dependencies. +This is a patch release focused on dependency service updates, tighter source-project build analysis, and maintainability cleanups around ExceptionConverter and modern C# patterns. + +### Changed + +- Package dependencies upgraded: + - Codebelt.Extensions.Xunit (11.1.1 → 11.1.2), + - Codebelt.Extensions.Xunit.App (11.1.1 → 11.1.2), + - Cuemon.AspNetCore.Mvc (10.5.4 → 10.5.5), + - Cuemon.Core (10.5.4 → 10.5.5), + - Cuemon.Extensions.AspNetCore (10.5.4 → 10.5.5), + - Cuemon.Extensions.AspNetCore.Authentication (10.5.4 → 10.5.5), + - Cuemon.Extensions.AspNetCore.Mvc (10.5.4 → 10.5.5), + - Cuemon.Extensions.Core (10.5.4 → 10.5.5), + - Cuemon.Extensions.IO (10.5.4 → 10.5.5), + - Cuemon.IO (10.5.4 → 10.5.5), + - Microsoft.NET.Test.Sdk (18.7.0 → 18.8.1), +- Directory.Build.props now scopes multi-targeting, packaging, and signing to source projects, enables recommended analyzers with style enforcement, and sets MinVerTagPrefix, +- ExceptionConverter JSON parse path extracted into focused helpers, removing the legacy GlobalSuppressions file that only covered the old monolithic parser, +- Source and tests modernized to prefer using declarations and ThrowIfNull on supported TFMs, with a direct char compare for trailing-asterisk path matching in JDataResultExtensions, +- XML cref documentation simplified by dropping redundant `T:` prefixes on generic type references, +- DocFX nginx base image pin loosened from `1.31.2-alpine` to `1.31-alpine`. ## [10.1.5] - 2026-06-30 @@ -290,7 +310,7 @@ This major release is first and foremost focused on ironing out any wrinkles tha - JsonReaderResultExtensions class from the Codebelt.Extensions.Newtonsoft.Json namespace - JsonReaderParser class from the Codebelt.Extensions.Newtonsoft.Json namespace -[Unreleased]: https://github.com/codebeltnet/newtonsoft-json/compare/v10.1.5...HEAD +[10.1.6]: https://github.com/codebeltnet/newtonsoft-json/compare/v10.1.5...v10.1.6 [10.1.5]: https://github.com/codebeltnet/newtonsoft-json/compare/v10.1.4...v10.1.5 [10.1.4]: https://github.com/codebeltnet/newtonsoft-json/compare/v10.1.3...v10.1.4 [10.1.3]: https://github.com/codebeltnet/newtonsoft-json/compare/v10.1.2...v10.1.3