JsonDeserializationDynamicObject: retarget net10.0, drop dead packages, add return-type tests - #2159
Open
vladimir-pecanac-main wants to merge 1 commit into
Conversation
…urn-type tests Retarget all three projects from net6.0 to net10.0 and move the packages forward: Newtonsoft.Json 13.0.4, Microsoft.NET.Test.Sdk 18.9.0, xunit 2.9.3, xunit.runner.visualstudio 4.0.0, coverlet.collector 10.0.1, BenchmarkDotNet 0.15.8. Drop three dead package references: - System.Text.Json 6.0.2 in all three projects. It is in-box on net10.0 and pinning 6.0.2 there downgrades the shared-framework assembly. - Microsoft.AspNetCore.Mvc.Testing in the test project. The solution has no web host and no WebApplicationFactory. - BenchmarkDotNet.Diagnostics.Windows in the benchmark project. Unused, and Windows-only in a solution CI builds on Linux. Fix a benchmark that was not comparing like with like. NewtonsoftJsonAnonymousType called UsingAnonymousTypeWithDictionary, which reads two values out of a Dictionary<string, double>, while SystemTextJsonAnonymousType reads one value out of a nested anonymous type. It now calls UsingAnonymousType, so both benchmarks do the same work. Add ReturnTypeTests, asserting what each API actually returns for an object, an array, a string, a number, a boolean and the null literal. The interesting case: DeserializeObject<dynamic> gives CLR primitives for scalars (string, double, bool), not JValue, and a real null reference for the null literal, while DeserializeObject<JToken> gives JValue for all of them. Tighten the existing native dynamic test from Assert.ThrowsAny<Exception> to Assert.Throws<RuntimeBinderException>, which is the exception member access on a boxed JsonElement actually raises.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Updates the
json-csharp/JsonDeserializationDynamicObjectsample for the .NET 10 republish of How to Deserialize JSON Into Dynamic Object in C#.Retarget and package lift
All three projects move from
net6.0tonet10.0. Newtonsoft.Json 13.0.1 to 13.0.4, Microsoft.NET.Test.Sdk 16.11.0 to 18.9.0, xunit 2.4.1 to 2.9.3, xunit.runner.visualstudio 2.4.3 to 4.0.0, coverlet.collector 3.1.0 to 10.0.1, BenchmarkDotNet 0.13.1 to 0.15.8.Three package references removed
System.Text.Json6.0.2, from all three projects. It is in-box from the shared framework onnet10.0, and pinning 6.0.2 on anet10.0TFM downgrades the in-box assembly. Verified: the solution builds and every test passes with noSystem.Text.Jsonpackage reference at all.Microsoft.AspNetCore.Mvc.Testing6.0.2, from the test project. The solution has no web host and noWebApplicationFactory; the reference is inherited from a template and dragged an ASP.NET Core dependency into a pure-serialization sample.BenchmarkDotNet.Diagnostics.Windows, from the benchmark project. Unused, and Windows-only in a solution CI builds on Linux.Benchmark methodology fix
NewtonsoftJsonAnonymousTypecalledUsingAnonymousTypeWithDictionary, which deserializesRatinginto aDictionary<string, double>and reads two values out of it. The native benchmark it is compared against,SystemTextJsonAnonymousType, deserializesRatinginto a nested anonymous type and reads one value. The two were not measuring the same operation, and the published article presents them side by side as if they were.NewtonsoftJsonAnonymousTypenow callsUsingAnonymousType, so both do the same work.Worth noting for anyone reading the article's table: the fix does not flip that row. Newtonsoft's anonymous type is still faster than the native one on this workload (1.57x the baseline against 1.73x) now that the comparison is like for like, so the result is real rather than an artefact of the mismatch.
New
ReturnTypeTestsAsserts what each API actually returns for a JSON object, array, string, number, boolean and the
nullliteral. The interesting cases, all measured on this branch:JsonConvert.DeserializeObject<dynamic>returns CLR primitives for scalars,string/double/bool, notJValue.JsonConvert.DeserializeObject<dynamic>("null")returns a real null reference, whileJsonConvert.DeserializeObject<JToken>("null")returns aJValueofJTokenType.Null. Declaring the result asJTokenis a different contract, not a free one.JsonSerializer.Deserialize<dynamic>always returns a boxedJsonElement, discriminated byValueKind, except for thenullliteral.These back a lookup table the article gains, so the type names are enforced by CI instead of stated in prose.
One existing test tightened
NativeJsonUnitTestassertedAssert.ThrowsAny<Exception>for member access on the boxedJsonElement. It is specificallyMicrosoft.CSharp.RuntimeBinder.RuntimeBinderException, so the test now says so.Build and tests: Release, 0 warnings, 0 errors, 33/33 passing on SDK 10.0.302 / runtime 10.0.10.