diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs index a6896e3d30dd32..7d59d15b71930f 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.AsyncEnumerable.cs @@ -1,4 +1,4 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Runtime.CompilerServices; +using System.Text.Json.Serialization.Metadata; using System.Threading; using System.Threading.Tasks; using Xunit; @@ -26,7 +27,7 @@ public async Task WriteRootLevelAsyncEnumerable(IEnumerable JsonSerializerOptions options = Serializer.CreateOptions(makeReadOnly: false); options.DefaultBufferSize = bufferSize; - string expectedJson = JsonSerializer.Serialize(source, options); + string expectedJson = await Serializer.SerializeWrapper(source, options); using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); @@ -49,7 +50,7 @@ public async Task WriteNestedAsyncEnumerable(IEnumerable sou JsonSerializerOptions options = Serializer.CreateOptions(makeReadOnly: false); options.DefaultBufferSize = bufferSize; - string expectedJson = JsonSerializer.Serialize(new EnumerableDto { Data = source }, options); + string expectedJson = await Serializer.SerializeWrapper(new EnumerableDto { Data = source }, options); using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); @@ -75,7 +76,7 @@ public async Task WriteNestedAsyncEnumerable_Nullable(IEnumerable, bool)?>((source, false), options); + string expectedJson = await Serializer.SerializeWrapper<(IEnumerable, bool)?>((source, false), options); using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); @@ -94,7 +95,7 @@ public async Task WriteAsyncEnumerable_CancellationToken_IsPassedToAsyncEnumerat using var cts = new CancellationTokenSource(); IAsyncEnumerable value = CreateEnumerable(); - await JsonSerializer.SerializeAsync(utf8Stream, value, Serializer.DefaultOptions, cancellationToken: cts.Token); + await JsonSerializer.SerializeAsync(utf8Stream, value, Serializer.DefaultOptions.GetTypeInfo>(), cts.Token); Assert.Equal("[1,2]", utf8Stream.AsString()); async IAsyncEnumerable CreateEnumerable([EnumeratorCancellation] CancellationToken cancellationToken = default) @@ -128,7 +129,7 @@ public async Task WriteAsyncEnumerable_LongRunningEnumeration_Cancellation( using var utf8Stream = new Utf8MemoryStream(); using var cts = new CancellationTokenSource(delay: TimeSpan.FromMilliseconds(cancellationTokenSourceDelayMilliseconds)); await Assert.ThrowsAsync(async () => - await JsonSerializer.SerializeAsync(utf8Stream, longRunningEnumerable, Serializer.DefaultOptions, cancellationToken: cts.Token)); + await JsonSerializer.SerializeAsync(utf8Stream, longRunningEnumerable, Serializer.DefaultOptions.GetTypeInfo>(), cts.Token)); Assert.Equal(1, longRunningEnumerable.TotalCreatedEnumerators); Assert.Equal(1, longRunningEnumerable.TotalDisposedEnumerators); @@ -168,7 +169,7 @@ public async Task WriteSequentialNestedAsyncEnumerables(IEnumerable { Data1 = source, Data2 = source }, options); + string expectedJson = await Serializer.SerializeWrapper(new EnumerableDtoWithTwoProperties { Data1 = source, Data2 = source }, options); using var stream = new Utf8MemoryStream(); var asyncEnumerable = new MockedAsyncEnumerable(source, delayInterval); @@ -192,7 +193,7 @@ public async Task WriteAsyncEnumerableOfAsyncEnumerables(IEnumerable(source, delayInterval); var outerAsyncEnumerable = @@ -213,16 +214,16 @@ public async Task WriteAsyncEnumerableOfAsyncEnumerables(IEnumerable asyncEnumerable = new MockedAsyncEnumerable(Enumerable.Range(1, 10)); - Assert.Throws(() => JsonSerializer.Serialize(asyncEnumerable, Serializer.DefaultOptions)); - Assert.Throws(() => JsonSerializer.Serialize(new MemoryStream(), asyncEnumerable, Serializer.DefaultOptions)); + Assert.Throws(() => JsonSerializer.Serialize(asyncEnumerable, Serializer.DefaultOptions.GetTypeInfo>())); + Assert.Throws(() => JsonSerializer.Serialize(new MemoryStream(), asyncEnumerable, Serializer.DefaultOptions.GetTypeInfo>())); } [Fact] public void WriteNestedAsyncEnumerableSync_ThrowsNotSupportedException() { IAsyncEnumerable asyncEnumerable = new MockedAsyncEnumerable(Enumerable.Range(1, 10)); - Assert.Throws(() => JsonSerializer.Serialize(new AsyncEnumerableDto { Data = asyncEnumerable }, Serializer.DefaultOptions)); - Assert.Throws(() => JsonSerializer.Serialize(new MemoryStream(), new AsyncEnumerableDto { Data = asyncEnumerable }, Serializer.DefaultOptions)); + Assert.Throws(() => JsonSerializer.Serialize(new AsyncEnumerableDto { Data = asyncEnumerable }, Serializer.DefaultOptions.GetTypeInfo>())); + Assert.Throws(() => JsonSerializer.Serialize(new MemoryStream(), new AsyncEnumerableDto { Data = asyncEnumerable }, Serializer.DefaultOptions.GetTypeInfo>())); } [Fact] @@ -331,7 +332,7 @@ public async Task RegressionTest_DisposingEnumeratorOnPendingMoveNextAsyncOperat // Regression test for https://github.com/dotnet/runtime/issues/57360 using var stream = new Utf8MemoryStream(); using var cts = new CancellationTokenSource(millisecondsDelay: 1000); - await Assert.ThrowsAsync(async () => await JsonSerializer.SerializeAsync(stream, GetNumbersAsync(), Serializer.DefaultOptions, cancellationToken: cts.Token)); + await Assert.ThrowsAsync(async () => await JsonSerializer.SerializeAsync(stream, GetNumbersAsync(), Serializer.DefaultOptions.GetTypeInfo>(), cts.Token)); static async IAsyncEnumerable GetNumbersAsync() { @@ -417,13 +418,6 @@ public async Task WriteNestedAsyncEnumerableInsideAsyncEnumerable_InnerEnumerato return; } - // This test requires reflection to serialize custom IAsyncEnumerable types; - // skip in source-gen contexts where reflection is disabled. - if (!JsonSerializer.IsReflectionEnabledByDefault) - { - return; - } - var events = new List(); var inner1 = new DisposalTrackingAsyncEnumerable( new[] { 1, 2 }, "Inner1", events, asyncDisposal); @@ -434,7 +428,7 @@ public async Task WriteNestedAsyncEnumerableInsideAsyncEnumerable_InnerEnumerato new IAsyncEnumerable[] { inner1, inner2 }, "Outer", events, asyncDisposal); using var stream = new Utf8MemoryStream(); - await JsonSerializer.SerializeAsync>>(stream, outer); + await JsonSerializer.SerializeAsync(stream, (IAsyncEnumerable>)outer, Serializer.DefaultOptions.GetTypeInfo>>()); JsonTestHelper.AssertJsonEqual("[[1,2],[3,4]]", stream.AsString()); diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs index d98d579ecae79d..b6b4559c9c86f6 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.Dictionary.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.Encodings.Web; using System.Threading.Tasks; @@ -731,6 +732,8 @@ public override void Write(Utf8JsonWriter writer, MyClass value, JsonSerializerO // levels deep, along with matching JSON, encompassing the various planes of // dictionaries that can be combined: generic, non-generic, BCL, user-derived, // immutable, mutable, readonly, concurrent, specialized. + [RequiresUnreferencedCode("Uses Type.MakeGenericType to construct nested dictionary types.")] + [RequiresDynamicCode("Uses Type.MakeGenericType to construct nested dictionary types.")] private static IEnumerable<(Type, string)> NestedDictionaryTypeData() { string testJson = """{"Key":1}"""; @@ -793,6 +796,8 @@ public override void Write(Utf8JsonWriter writer, MyClass value, JsonSerializerO #endif [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/66220", TestPlatforms.iOS | TestPlatforms.tvOS | TestPlatforms.MacCatalyst)] + [RequiresUnreferencedCode("Uses Type.MakeGenericType to construct nested dictionary types.")] + [RequiresDynamicCode("Uses Type.MakeGenericType to construct nested dictionary types.")] public async Task NestedDictionariesRoundtrip() { JsonSerializerOptions options = new JsonSerializerOptions(); @@ -1300,11 +1305,11 @@ public async Task IgnoreDictionaryPropertyWithDifferentOrdering() await VerifyIgnore(false, true, false, addMissing: true); } - private async Task VerifyIgnore(bool skip1, bool skip2, bool skip3, bool addMissing = false) + private async Task VerifyIgnore<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(bool skip1, bool skip2, bool skip3, bool addMissing = false) { static IDictionary GetProperty(T objectToVerify, string propertyName) { - return (IDictionary)objectToVerify.GetType().GetProperty(propertyName).GetValue(objectToVerify); + return (IDictionary)typeof(T).GetProperty(propertyName).GetValue(objectToVerify); } void Verify(T objectToVerify) diff --git a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.KeyValuePair.cs b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.KeyValuePair.cs index 4f3fdc487b060c..e7b58778678f51 100644 --- a/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.KeyValuePair.cs +++ b/src/libraries/System.Text.Json/tests/Common/CollectionTests/CollectionTests.KeyValuePair.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Text.Encodings.Web; using System.Threading.Tasks; using Xunit; @@ -396,7 +397,7 @@ private class TrailingAngleBracketPolicy : JsonNamingPolicy [Theory] [InlineData(typeof(KeyNameNullPolicy), "Key")] [InlineData(typeof(ValueNameNullPolicy), "Value")] - public async Task InvalidPropertyNameFail(Type policyType, string offendingProperty) + public async Task InvalidPropertyNameFail([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type policyType, string offendingProperty) { var options = new JsonSerializerOptions { diff --git a/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.Cache.cs b/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.Cache.cs index cf05d3a88d4d1f..6cb3ba3e1616d9 100644 --- a/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.Cache.cs +++ b/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.Cache.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Threading.Tasks; using Xunit; @@ -34,20 +35,20 @@ async Task DeserializeObjectAsync(string json, Type type, JsonSerializerOptions ((ITestClassWithParameterizedCtor)obj).Verify(); } - async Task DeserializeObjectMinimalAsync(Type type, JsonSerializerOptions options) + async Task DeserializeObjectMinimalAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, JsonSerializerOptions options) { string json = (string)type.GetProperty("s_json_minimal").GetValue(null); var obj = await Serializer.DeserializeWrapper(json, type, options); ((ITestClassWithParameterizedCtor)obj).VerifyMinimal(); }; - async Task DeserializeObjectFlippedAsync(Type type, JsonSerializerOptions options) + async Task DeserializeObjectFlippedAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, JsonSerializerOptions options) { string json = (string)type.GetProperty("s_json_flipped").GetValue(null); await DeserializeObjectAsync(json, type, options); }; - async Task DeserializeObjectNormalAsync(Type type, JsonSerializerOptions options) + async Task DeserializeObjectNormalAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, JsonSerializerOptions options) { string json = (string)type.GetProperty("s_json").GetValue(null); await DeserializeObjectAsync(json, type, options); @@ -59,7 +60,7 @@ async Task SerializeObject(Type type, JsonSerializerOptions options) await Serializer.SerializeWrapper(obj, options); }; - async Task RunTestAsync(Type type) + async Task RunTestAsync([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // Use local options to avoid obtaining already cached metadata from the default options. JsonSerializerOptions options = Serializer.CreateOptions(); @@ -129,7 +130,7 @@ public async Task PropertyCacheWithMinInputsLast() [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/45464", ~RuntimeConfiguration.Release)] public async Task MultipleTypes() { - async Task Serialize(object[] args) + async Task Serialize<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(object[] args) { Type type = typeof(T); @@ -145,7 +146,7 @@ async Task DeserializeAsync(string json) obj.Verify(); }; - async Task RunTestAsync(T testObj, object[] args) + async Task RunTestAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(T testObj, object[] args) { // Get the test json with the default options to avoid cache pollution of DeserializeAsync() below. ((ITestClass)testObj).Initialize(); diff --git a/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.ParameterMatching.cs b/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.ParameterMatching.cs index 879894d680761f..217a5d7224ed0e 100644 --- a/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.ParameterMatching.cs +++ b/src/libraries/System.Text.Json/tests/Common/ConstructorTests/ConstructorTests.ParameterMatching.cs @@ -731,7 +731,7 @@ public async Task ConstructorHandlingHonorsCustomConverters() [Fact] public async Task CanDeserialize_ObjectWith_Ctor_With_64_Params() { - async Task RunTestAsync() + async Task RunTestAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>() { StringBuilder sb = new StringBuilder(); sb.Append("{"); diff --git a/src/libraries/System.Text.Json/tests/Common/ExtensionDataTests.cs b/src/libraries/System.Text.Json/tests/Common/ExtensionDataTests.cs index dbf0bae31d62d8..dc823633137b01 100644 --- a/src/libraries/System.Text.Json/tests/Common/ExtensionDataTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/ExtensionDataTests.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text.Encodings.Web; using System.Text.Json.Nodes; @@ -1353,7 +1354,7 @@ public class ClassWithExtensionPropertyCustomIImmutableJsonElement [InlineData(typeof(ClassWithExtensionPropertyNoGenericParameters))] [InlineData(typeof(ClassWithExtensionPropertyOneGenericParameter))] [InlineData(typeof(ClassWithExtensionPropertyThreeGenericParameters))] - public async Task DeserializeIntoGenericDictionaryParameterCount(Type type) + public async Task DeserializeIntoGenericDictionaryParameterCount([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { object obj = await Serializer.DeserializeWrapper("{\"hello\":\"world\"}", type); diff --git a/src/libraries/System.Text.Json/tests/Common/JsonCreationHandlingTests.Object.cs b/src/libraries/System.Text.Json/tests/Common/JsonCreationHandlingTests.Object.cs index bd91642bbe7f70..c74c9b1902ca39 100644 --- a/src/libraries/System.Text.Json/tests/Common/JsonCreationHandlingTests.Object.cs +++ b/src/libraries/System.Text.Json/tests/Common/JsonCreationHandlingTests.Object.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; +using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Text.Json.Serialization.Tests; @@ -825,10 +827,11 @@ public class ClassWithRecursiveRequiredProperty public ClassWithRecursiveRequiredProperty? Next { get; set; } } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [InlineData(typeof(SimpleClass))] [InlineData(typeof(SimpleClassWithSmallParametrizedCtor))] [InlineData(typeof(SimpleClassWithLargeParametrizedCtor))] + [RequiresDynamicCode("Uses Type.MakeGenericType to construct the generic type under test.")] public async Task CreationHandlingSetWithAttribute_CanPopulateOrSerialize_Callbacks(Type type) { Type finalType = typeof(ClassWithWritableProperty<>).MakeGenericType(type); @@ -879,7 +882,10 @@ async Task DeserializeAndEnsureCallbacksCalled(string json, int expectedTimesCal [InlineData(typeof(ClassImplementingInterfaceWithPopulateOnTypeWithInterfaceProperty), null, false)] [InlineData(typeof(IInterfaceWithInterfaceProperty), typeof(ClassImplementingInterfaceWithInterfaceProperty), false)] [InlineData(typeof(ClassImplementingInterfaceWithInterfaceProperty), null, true)] - public async Task CreationHandlingSetWithAttributeOnType_Interfaces(Type typeToDeserialize, Type? typeToCreate, bool shouldPopulate) + public async Task CreationHandlingSetWithAttributeOnType_Interfaces( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type typeToDeserialize, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type? typeToCreate, + bool shouldPopulate) { JsonSerializerOptions options = Serializer.CreateOptions(modifier: ti => { @@ -1169,7 +1175,7 @@ public class ClassWithInvalidPropertyAnnotation [Theory] [InlineData(typeof(ClassWithParameterizedConstructorWithPopulateProperty))] [InlineData(typeof(ClassWithParameterizedConstructorWithPopulateType))] - public async Task ClassWithParameterizedCtor_UsingPopulateConfiguration_ThrowsNotSupportedException(Type type) + public async Task ClassWithParameterizedCtor_UsingPopulateConfiguration_ThrowsNotSupportedException([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type type) { object instance = Activator.CreateInstance(type, "Jim"); string json = """{"Username":"Jim","PhoneNumbers":["123456"]}"""; diff --git a/src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.cs b/src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.cs index 0e6770c02ddc56..190166ef19c3f8 100644 --- a/src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/JsonSchemaExporterTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Runtime.Serialization; @@ -11,6 +12,7 @@ using System.Text.Json.Serialization.Tests; using System.Xml.Linq; using Json.Schema; +using Microsoft.DotNet.XUnitExtensions; using Xunit; using Xunit.Sdk; @@ -38,8 +40,10 @@ public void TestTypes_GeneratesExpectedJsonSchema(ITestData testData) AssertValidJsonSchema(testData.Type, testData.ExpectedJsonSchema, schema); } - [Theory] + [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] [MemberData(nameof(GetTestDataUsingAllValues))] + [RequiresUnreferencedCode("Uses reflection-based JsonSerializer.SerializeToNode(object, Type, options).")] + [RequiresDynamicCode("Uses reflection-based JsonSerializer.SerializeToNode(object, Type, options).")] public void TestTypes_SerializedValueMatchesGeneratedSchema(ITestData testData) { JsonSerializerOptions options = testData.SerializerOptions is { } opts @@ -215,8 +219,9 @@ public void JsonSchemaExporterOptions_Default_IsSame() Assert.Same(JsonSchemaExporterOptions.Default, JsonSchemaExporterOptions.Default); } -#if !BUILDING_SOURCE_GENERATOR_TESTS - [Fact] + [ConditionalFact(typeof(JsonSerializer), nameof(JsonSerializer.IsReflectionEnabledByDefault))] + [RequiresUnreferencedCode("Uses private reflection to access System.Text.Json converter internals.")] + [RequiresDynamicCode("Uses private reflection to access System.Text.Json converter internals.")] public void LegacySchemaExporter_CanAccessReflectedMembers() { // A number of libraries such as Microsoft.Extensions.AI and Semantic Kernel @@ -259,7 +264,6 @@ record PocoWithProperty(int Value); [JsonSerializable(typeof(PocoWithProperty))] partial class PocoWithPropertyContext : JsonSerializerContext; -#endif protected void AssertValidJsonSchema(Type type, string expectedJsonSchema, JsonNode actualJsonSchema) { @@ -326,6 +330,6 @@ private EvaluationResults EvaluateSchemaCore(JsonNode schema, JsonNode? instance }; private string FormatJson(JsonNode? node) => - JsonSerializer.Serialize(node, _indentedOptions); + JsonSerializer.Serialize(node, _indentedOptions.GetTypeInfo()); } } diff --git a/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapper.cs b/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapper.cs index 710304c9d73965..f453f04d807d3a 100644 --- a/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapper.cs +++ b/src/libraries/System.Text.Json/tests/Common/JsonSerializerWrapper.cs @@ -73,7 +73,14 @@ public abstract IAsyncEnumerable DeserializeAsyncEnumerable( public virtual JsonTypeInfo GetTypeInfo(Type type, JsonSerializerOptions? options = null, bool mutable = false) { options ??= DefaultOptions; +#if BUILDING_SOURCE_GENERATOR_TESTS + // In the source generator test project the options are always backed by a + // JsonSerializerContext resolver, so there is never a missing resolver to populate. + // Use the parameterless overload which is safe for trimming and Native AOT. + options.MakeReadOnly(); +#else options.MakeReadOnly(populateMissingResolver: true); +#endif return mutable ? options.TypeInfoResolver.GetTypeInfo(type, options) : options.GetTypeInfo(type); } diff --git a/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs b/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs index 7fbdfef6a7a14c..95535fa97de428 100644 --- a/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs +++ b/src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text.Json.Serialization; @@ -157,6 +158,7 @@ static string BuildJsonPath(Stack path) } } + [RequiresUnreferencedCode("AssertOptionsEqual uses reflection to enumerate JsonSerializerOptions properties.")] public static void AssertOptionsEqual(JsonSerializerOptions expected, JsonSerializerOptions actual) { foreach (PropertyInfo property in typeof(JsonSerializerOptions).GetProperties(BindingFlags.Public | BindingFlags.Instance)) diff --git a/src/libraries/System.Text.Json/tests/Common/MetadataTests.Options.cs b/src/libraries/System.Text.Json/tests/Common/MetadataTests.Options.cs index 8341415bd84815..9b147d0d40914a 100644 --- a/src/libraries/System.Text.Json/tests/Common/MetadataTests.Options.cs +++ b/src/libraries/System.Text.Json/tests/Common/MetadataTests.Options.cs @@ -89,20 +89,21 @@ public void OptionsImmutableAfterBinding() [Fact] public void PassingImmutableOptionsThrowsException() { - JsonSerializerOptions defaultOptions = JsonSerializerOptions.Default; - Assert.Throws(() => new MyJsonContext(defaultOptions)); + JsonSerializerOptions options = Serializer.DefaultOptions; + Assert.True(options.IsReadOnly); + + Assert.Throws(() => new MyJsonContext(options)); } [Fact] public void PassingWrongOptionsInstanceToResolverThrowsException() { - JsonSerializerOptions defaultOptions = JsonSerializerOptions.Default; JsonSerializerOptions contextOptions = new(); IJsonTypeInfoResolver context = new EmptyContext(contextOptions); Assert.IsAssignableFrom>(context.GetTypeInfo(typeof(int), contextOptions)); Assert.IsAssignableFrom>(context.GetTypeInfo(typeof(int), null)); - Assert.Throws(() => context.GetTypeInfo(typeof(int), defaultOptions)); + Assert.Throws(() => context.GetTypeInfo(typeof(int), Serializer.DefaultOptions)); } private class MyJsonContext : JsonSerializerContext @@ -127,7 +128,10 @@ private class EmptyContext : JsonSerializerContext { public EmptyContext(JsonSerializerOptions options) : base(options) { } protected override JsonSerializerOptions? GeneratedSerializerOptions => null; - public override JsonTypeInfo? GetTypeInfo(Type type) => JsonTypeInfo.CreateJsonTypeInfo(type, Options); + public override JsonTypeInfo? GetTypeInfo(Type type) + => type == typeof(int) + ? JsonMetadataServices.CreateValueInfo(Options, JsonMetadataServices.Int32Converter) + : null; } } } diff --git a/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs b/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs index 4aaf962626c994..923a905d6730bb 100644 --- a/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/MetadataTests.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text.Json.Serialization.Metadata; @@ -37,7 +38,7 @@ public void TypeWithoutConstructor_TypeInfoReportsNullCtorProvider(Type type) [InlineData(typeof(ClassWithParameterizedCtor))] [InlineData(typeof(ClassWithMultipleConstructors))] [InlineData(typeof(DerivedClassWithShadowingProperties))] - public void TypeWithConstructor_TypeInfoReportsExpectedCtorProvider(Type typeWithCtor) + public void TypeWithConstructor_TypeInfoReportsExpectedCtorProvider([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeWithCtor) { ConstructorInfo? expectedCtor = typeWithCtor.GetConstructors(BindingFlags.Public | BindingFlags.Instance) .OrderByDescending(ctor => ctor.GetCustomAttribute() is not null) @@ -56,7 +57,7 @@ public void TypeWithConstructor_TypeInfoReportsExpectedCtorProvider(Type typeWit [InlineData(typeof(ClassWithParameterizedCtor))] [InlineData(typeof(ClassWithMultipleConstructors))] [InlineData(typeof(DerivedClassWithShadowingProperties))] - public void TypeWithConstructor_SettingCtorDelegate_ResetsCtorAttributeProvider(Type typeWithCtor) + public void TypeWithConstructor_SettingCtorDelegate_ResetsCtorAttributeProvider([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type typeWithCtor) { JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithCtor, mutable: true); Assert.NotNull(typeInfo.ConstructorAttributeProvider); @@ -97,6 +98,7 @@ public void JsonPropertyInfo_DeclaringType_HasExpectedValue(Type typeWithPropert [InlineData(typeof(ClassWithMultipleConstructors))] [InlineData(typeof(DerivedClassWithShadowingProperties))] [InlineData(typeof(IDerivedInterface))] + [RequiresUnreferencedCode("Uses reflection to resolve the member backing each JsonPropertyInfo.")] public void JsonPropertyInfo_AttributeProvider_HasExpectedValue(Type typeWithProperties) { JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithProperties); @@ -129,7 +131,7 @@ public void TypeWithoutConstructor_JsonPropertyInfo_AssociatedParameter_IsNull(T [InlineData(typeof(StructWithParameterizedCtor))] [InlineData(typeof(ClassWithMultipleConstructors))] [InlineData(typeof(DerivedClassWithShadowingProperties))] - public void TypeWithConstructor_JsonPropertyInfo_AssociatedParameter_MatchesCtorParams(Type typeWithCtor) + public void TypeWithConstructor_JsonPropertyInfo_AssociatedParameter_MatchesCtorParams([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type typeWithCtor) { ConstructorInfo? expectedCtor = typeWithCtor.GetConstructors(BindingFlags.Public | BindingFlags.Instance) .OrderByDescending(ctor => ctor.GetCustomAttribute() is not null) @@ -243,7 +245,7 @@ public void TypeWithInitOnlyAndRequiredMembers_OnlyRequiredHasAssociatedParamete [InlineData(typeof(ClassWithInitOnlyProperty))] [InlineData(typeof(ClassWithMultipleConstructors))] [InlineData(typeof(DerivedClassWithShadowingProperties))] - public void TypeWithConstructor_SettingCtorDelegate_ResetsAssociatedParameters(Type typeWithCtor) + public void TypeWithConstructor_SettingCtorDelegate_ResetsAssociatedParameters([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type typeWithCtor) { JsonTypeInfo typeInfo = Serializer.GetTypeInfo(typeWithCtor, mutable: true); Assert.NotEmpty(typeInfo.Properties); @@ -424,6 +426,7 @@ public void CollectionWithRefStructElement_Serialization() return defaultValue; } + [RequiresUnreferencedCode("Uses Type.GetMember/Type.GetInterfaces reflection to resolve the member backing a JsonPropertyInfo.")] private static MemberInfo? ResolveMember(Type type, string name) { MemberInfo? result = type.GetMember(name, BindingFlags.Instance | BindingFlags.Public).FirstOrDefault(); diff --git a/src/libraries/System.Text.Json/tests/Common/NullableAnnotationsTests.cs b/src/libraries/System.Text.Json/tests/Common/NullableAnnotationsTests.cs index 5ce2030301c0f9..f0e120986ae179 100644 --- a/src/libraries/System.Text.Json/tests/Common/NullableAnnotationsTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/NullableAnnotationsTests.cs @@ -22,7 +22,7 @@ protected NullableAnnotationsTests(JsonSerializerWrapper serializerUnderTest) [Theory] [MemberData(nameof(GetTypesWithNonNullablePropertyGetter))] - public async Task WriteNullFromNotNullablePropertyGetter_EnforcedNullability_ThrowsJsonException(Type type, string propertyName) + public async Task WriteNullFromNotNullablePropertyGetter_EnforcedNullability_ThrowsJsonException([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, string propertyName) { object value = Activator.CreateInstance(type)!; @@ -34,7 +34,7 @@ public async Task WriteNullFromNotNullablePropertyGetter_EnforcedNullability_Thr [Theory] [MemberData(nameof(GetTypesWithNonNullablePropertyGetter))] - public async Task WriteNullFromNotNullablePropertyGetter_IgnoredNullability_Succeeds(Type type, string _) + public async Task WriteNullFromNotNullablePropertyGetter_IgnoredNullability_Succeeds([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, string _) { object value = Activator.CreateInstance(type)!; string json = await Serializer.SerializeWrapper(value, type, s_optionsWithIgnoredNullability); @@ -43,7 +43,7 @@ public async Task WriteNullFromNotNullablePropertyGetter_IgnoredNullability_Succ [Theory] [MemberData(nameof(GetTypesWithNonNullablePropertyGetter))] - public async Task WriteNullFromNotNullablePropertyGetter_EnforcedNullability_DisabledFlag_Succeeds(Type type, string propertyName) + public async Task WriteNullFromNotNullablePropertyGetter_EnforcedNullability_DisabledFlag_Succeeds([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, string propertyName) { object value = Activator.CreateInstance(type)!; JsonTypeInfo typeInfo = Serializer.GetTypeInfo(type, s_optionsWithEnforcedNullability, mutable: true); @@ -80,7 +80,7 @@ public static IEnumerable GetTypesWithNonNullablePropertyGetter() [Theory] [MemberData(nameof(GetTypesWithNullablePropertyGetter))] - public async Task WriteNullFromNullablePropertyGetter_EnforcedNullability_Succeeds(Type type, string _) + public async Task WriteNullFromNullablePropertyGetter_EnforcedNullability_Succeeds([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, string _) { object value = Activator.CreateInstance(type)!; string json = await Serializer.SerializeWrapper(value, type, s_optionsWithEnforcedNullability); @@ -89,7 +89,7 @@ public async Task WriteNullFromNullablePropertyGetter_EnforcedNullability_Succee [Theory] [MemberData(nameof(GetTypesWithNullablePropertyGetter))] - public async Task WriteNullFromNullablePropertyGetter_IgnoredNullability_Succeeds(Type type, string _) + public async Task WriteNullFromNullablePropertyGetter_IgnoredNullability_Succeeds([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, string _) { object value = Activator.CreateInstance(type)!; string json = await Serializer.SerializeWrapper(value, type, s_optionsWithIgnoredNullability); @@ -98,7 +98,7 @@ public async Task WriteNullFromNullablePropertyGetter_IgnoredNullability_Succeed [Theory] [MemberData(nameof(GetTypesWithNullablePropertyGetter))] - public async Task WriteNullFromNullablePropertyGetter_EnforcedNullability_EnabledFlag_ThrowsJsonException(Type type, string propertyName) + public async Task WriteNullFromNullablePropertyGetter_EnforcedNullability_EnabledFlag_ThrowsJsonException([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, string propertyName) { object value = Activator.CreateInstance(type)!; JsonTypeInfo typeInfo = Serializer.GetTypeInfo(type, s_optionsWithEnforcedNullability, mutable: true); diff --git a/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs b/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs index e42b9c48323ae9..3858cb0c15b657 100644 --- a/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; @@ -374,6 +375,8 @@ public class Class_With_ListsOfBoxedNonNumbers } [Fact] + [RequiresUnreferencedCode("Uses Activator.CreateInstance to construct non-generic collection types.")] + [RequiresDynamicCode("Uses Activator.CreateInstance to construct non-generic collection types.")] public async Task Number_AsCollectionElement_RoundTrip() { await RunAsCollectionElementTest(JsonNumberTestData.Bytes); @@ -419,6 +422,8 @@ public async Task Number_AsCollectionElement_RoundTrip() } } + [RequiresUnreferencedCode("Uses Activator.CreateInstance to construct non-generic collection types.")] + [RequiresDynamicCode("Uses Activator.CreateInstance to construct non-generic collection types.")] private async Task RunAsCollectionElementTest(List numbers) { StringBuilder jsonBuilder_NumbersAsNumbers = new StringBuilder(); @@ -548,6 +553,8 @@ private void AssertIEnumerableEqual(IEnumerable list1, IEnumerable list Assert.False(enumerator2.MoveNext()); } + [RequiresUnreferencedCode("Uses Activator.CreateInstance to construct non-generic collection types.")] + [RequiresDynamicCode("Uses Activator.CreateInstance to construct non-generic collection types.")] private async Task RunAllCollectionsRoundTripTest(string json) { foreach (Type type in CollectionTestTypes.DeserializableGenericEnumerableTypes()) @@ -585,10 +592,13 @@ private async Task RunAllCollectionsRoundTripTest(string json) serialized = await Serializer.SerializeWrapper(list, s_optionReadAndWriteFromStr); Assert.Equal(json, serialized); - // Serialize instance which is a collection of numbers (not JsonElements). - obj = Activator.CreateInstance(type, new[] { list }); - serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr); - Assert.Equal(json, serialized); + if (PlatformDetection.IsNotNativeAot) + { + // Serialize instance which is a collection of numbers (not JsonElements). + obj = Activator.CreateInstance(type, new[] { list }); + serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr); + Assert.Equal(json, serialized); + } } } @@ -636,6 +646,8 @@ private void AssertDictionaryElements_StringValues(string serialized) [Fact] [ActiveIssue("https://github.com/dotnet/runtime/issues/39674", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))] [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/45464", ~RuntimeConfiguration.Release)] + [RequiresUnreferencedCode("Uses Activator.CreateInstance to construct non-generic dictionary types.")] + [RequiresDynamicCode("Uses Activator.CreateInstance to construct non-generic dictionary types.")] public async Task DictionariesRoundTrip() { await RunAllDictionariessRoundTripTest(JsonNumberTestData.ULongs); @@ -643,6 +655,8 @@ public async Task DictionariesRoundTrip() await RunAllDictionariessRoundTripTest(JsonNumberTestData.Doubles); } + [RequiresUnreferencedCode("Uses Activator.CreateInstance to construct non-generic dictionary types.")] + [RequiresDynamicCode("Uses Activator.CreateInstance to construct non-generic dictionary types.")] private async Task RunAllDictionariessRoundTripTest(List numbers) { StringBuilder jsonBuilder_NumbersAsStrings = new StringBuilder(); @@ -669,14 +683,17 @@ private async Task RunAllDictionariessRoundTripTest(List numbers) JsonTestHelper.AssertJsonEqual(jsonNumbersAsStrings, await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr)); } - foreach (Type type in CollectionTestTypes.DeserializableNonGenericDictionaryTypes()) + if (PlatformDetection.IsNotNativeAot) { - Dictionary dict = await Serializer.DeserializeWrapper>(jsonNumbersAsStrings, s_optionReadAndWriteFromStr); + foreach (Type type in CollectionTestTypes.DeserializableNonGenericDictionaryTypes()) + { + Dictionary dict = await Serializer.DeserializeWrapper>(jsonNumbersAsStrings, s_optionReadAndWriteFromStr); - // Serialize instance which is a dictionary of numbers (not JsonElements). - object obj = Activator.CreateInstance(type, new[] { dict }); - string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr); - JsonTestHelper.AssertJsonEqual(jsonNumbersAsStrings, serialized); + // Serialize instance which is a dictionary of numbers (not JsonElements). + object obj = Activator.CreateInstance(type, new[] { dict }); + string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr); + JsonTestHelper.AssertJsonEqual(jsonNumbersAsStrings, serialized); + } } } @@ -2022,6 +2039,7 @@ await Assert.ThrowsAsync( s_optionReadFromStr)); } +#if !BUILDING_SOURCE_GENERATOR_TESTS [Fact] public void GetConverter_Int32_RespectsNumberHandling_AllowReadingFromString() { @@ -2345,5 +2363,6 @@ public void GetConverter_Single_RespectsNumberHandling_AllowNamedFloatingPointLi reader.Read(); reader.Read(); reader.Read(); Assert.True(float.IsNaN(converter.Read(ref reader, typeof(float), options))); } +#endif } } diff --git a/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.CustomTypeHierarchies.cs b/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.CustomTypeHierarchies.cs index aede0175f01406..c3d47893e33feb 100644 --- a/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.CustomTypeHierarchies.cs +++ b/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.CustomTypeHierarchies.cs @@ -34,7 +34,7 @@ public Task PolymorphicClass_TestData_Deserialization(PolymorphicClass.TestData testData.ExpectedJson, testData.ExpectedRoundtripValue, testData.ExpectedDeserializationException, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); public static IEnumerable Get_PolymorphicClass_TestData_Deserialization() => PolymorphicClass.GetSerializeTestData().Where(entry => entry.ExpectedJson != null).Select(entry => new object[] { entry }); @@ -58,7 +58,7 @@ public async Task PolymorphicClass_TestDataArray_Deserialization() .Where(entry => entry.ExpectedRoundtripValue is not null) .Select(entry => (entry.ExpectedJson, entry.ExpectedRoundtripValue)); - await TestMultiContextDeserialization(inputs, equalityComparer: PolymorphicEqualityComparer.Instance); + await TestMultiContextDeserialization(inputs, equalityComparer: CreateJsonEqualityComparer()); } [Theory] @@ -89,7 +89,7 @@ public Task PolymorphicClass_TestData_AllowOutOfOrderMetadata_Deserialization(Po testData.ExpectedRoundtripValue, testData.ExpectedDeserializationException, options: s_optionsWithAllowOutOfOrderMetadata, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); [Theory] [InlineData("""{"Number":42, "$type":"derivedClass1", "String": "str"}""", typeof(PolymorphicClass.DerivedClass1_TypeDiscriminator))] @@ -129,7 +129,7 @@ public async Task PolymorphicClass_AllowOutOfOrderMetadata_RejectsInvalidInputs( //-- - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Theory] [MemberData(nameof(Get_PolymorphicClass_CustomConfigWithBaseTypeFallback_TestData_Serialization))] public Task PolymorphicClass_CustomConfigWithBaseTypeFallback_TestData_Serialization(PolymorphicClass.TestData testData) => TestMultiContextSerialization( @@ -142,14 +142,14 @@ public Task PolymorphicClass_CustomConfigWithBaseTypeFallback_TestData_Serializa public static IEnumerable Get_PolymorphicClass_CustomConfigWithBaseTypeFallback_TestData_Serialization() => PolymorphicClass.GetSerializeTestData_CustomConfigWithBaseTypeFallback().Select(entry => new object[] { entry }); - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Theory] [MemberData(nameof(Get_PolymorphicClass_CustomConfigWithBaseTypeFallback_TestData_Deserialization))] public Task PolymorphicClass_CustomConfigWithBaseTypeFallback_TestData_Deserialization(PolymorphicClass.TestData testData) => TestMultiContextDeserialization( testData.ExpectedJson, testData.ExpectedRoundtripValue, testData.ExpectedSerializationException, - equalityComparer: PolymorphicEqualityComparer.Instance, + equalityComparer: CreateJsonEqualityComparer(), options: PolymorphicClass.CustomConfigWithBaseTypeFallback); public static IEnumerable Get_PolymorphicClass_CustomConfigWithBaseTypeFallback_TestData_Deserialization() @@ -157,7 +157,7 @@ public static IEnumerable Get_PolymorphicClass_CustomConfigWithBaseTyp .Where(entry => entry.ExpectedJson != null) .Select(entry => new object[] { entry }); - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Fact] public async Task PolymorphicClass_CustomConfigWithBaseTypeFallback_TestDataArray_Serialization() { IEnumerable<(PolymorphicClass Value, string ExpectedJson)> inputs = @@ -168,7 +168,7 @@ public async Task PolymorphicClass_CustomConfigWithBaseTypeFallback_TestDataArra await TestMultiContextSerialization(inputs, options: PolymorphicClass.CustomConfigWithBaseTypeFallback); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Fact] public async Task PolymorphicClass_CustomConfigWithBaseTypeFallbacks_TestDataArray_Deserialization() { IEnumerable<(string ExpectedJson, PolymorphicClass ExpectedRoundtripValue)> inputs = @@ -178,11 +178,11 @@ public async Task PolymorphicClass_CustomConfigWithBaseTypeFallbacks_TestDataArr await TestMultiContextDeserialization( inputs, - equalityComparer: PolymorphicEqualityComparer.Instance, + equalityComparer: CreateJsonEqualityComparer(), options: PolymorphicClass.CustomConfigWithBaseTypeFallback); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Theory] [InlineData("$['$type']", """{ "$type" : "derivedClass1", "Number" : 42 }""")] [InlineData("$._case", """{ "_case" : "derivedClass1", "_case" : "derivedClass1", "Number" : 42 }""")] [InlineData("$._case", """{ "_case" : "derivedClass1", "Number" : 42, "_case" : "derivedClass1"}""")] @@ -206,7 +206,7 @@ public async Task PolymorphicClass_CustomConfigWithBaseTypeFallback_InvalidTypeD //--- - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Theory] [MemberData(nameof(Get_PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestData_Serialization))] public Task PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestData_Serialization(PolymorphicClass.TestData testData) => TestMultiContextSerialization( @@ -219,14 +219,14 @@ public Task PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestData_Se public static IEnumerable Get_PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestData_Serialization() => PolymorphicClass.GetSerializeTestData_CustomConfigWithNearestAncestorFallback().Select(entry => new object[] { entry }); - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Theory] [MemberData(nameof(Get_PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestData_Deserialization))] public Task PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestData_Deserialization(PolymorphicClass.TestData testData) => TestMultiContextDeserialization( testData.ExpectedJson, testData.ExpectedRoundtripValue, testData.ExpectedDeserializationException, - equalityComparer: PolymorphicEqualityComparer.Instance, + equalityComparer: CreateJsonEqualityComparer(), options: PolymorphicClass.CustomConfigWithNearestAncestorFallback); public static IEnumerable Get_PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestData_Deserialization() @@ -234,7 +234,7 @@ public static IEnumerable Get_PolymorphicClass_CustomConfigWithNearest .Where(entry => entry.ExpectedJson != null) .Select(entry => new object[] { entry }); - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Fact] public async Task PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestDataArray_Serialization() { IEnumerable<(PolymorphicClass Value, string ExpectedJson)> inputs = @@ -245,7 +245,7 @@ public async Task PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestD await TestMultiContextSerialization(inputs, options: PolymorphicClass.CustomConfigWithNearestAncestorFallback); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Fact] public async Task PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestDataArray_Deserialization() { IEnumerable<(string ExpectedJson, PolymorphicClass ExpectedRoundtripValue)> inputs = @@ -255,11 +255,11 @@ public async Task PolymorphicClass_CustomConfigWithNearestAncestorFallback_TestD await TestMultiContextDeserialization( inputs, - equalityComparer: PolymorphicEqualityComparer.Instance, + equalityComparer: CreateJsonEqualityComparer(), options: PolymorphicClass.CustomConfigWithNearestAncestorFallback); } - [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Theory] [InlineData("$['$type']", """{ "$type" : "derivedClass1", "Number" : 42 }""")] [InlineData("$._case", """{ "_case" : "derivedClass1", "_case" : "derivedClass1", "Number" : 42 }""")] [InlineData("$._case", """{ "_case" : "derivedClass1", "Number" : 42, "_case" : "derivedClass1"}""")] @@ -302,22 +302,13 @@ public async Task PolymorphicClass_ConfigWithAbstractClass_ShouldThrowNotSupport [Fact] public async Task PolymorphicClass_ClearPolymorphismOptions_DoesNotUsePolymorphism() { - var options = new JsonSerializerOptions + JsonSerializerOptions options = Serializer.GetDefaultOptionsWithMetadataModifier(static jsonTypeInfo => { - TypeInfoResolver = new DefaultJsonTypeInfoResolver() + if (jsonTypeInfo.Type == typeof(PolymorphicClass)) { - Modifiers = - { - static jsonTypeInfo => - { - if (jsonTypeInfo.Type == typeof(PolymorphicClass)) - { - jsonTypeInfo.PolymorphismOptions = null; - } - } - } + jsonTypeInfo.PolymorphismOptions = null; } - }; + }); PolymorphicClass value = new PolymorphicClass.DerivedAbstractClass.DerivedClass { Number = 42, Boolean = true }; string json = await Serializer.SerializeWrapper(value, options); @@ -961,13 +952,13 @@ public async Task PolymorphicClass_WithDerivedPolymorphicClass_Deserialization_S await TestMultiContextDeserialization( json, expectedValueUsingBaseContract, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); var expectedValueUsingDerivedContract = new PolymorphicClass_WithDerivedPolymorphicClass.DerivedClass.DerivedClass2(); await TestMultiContextDeserialization( json, expectedValueUsingDerivedContract, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); } [JsonDerivedType(typeof(DerivedClass), "derivedClass")] @@ -1149,7 +1140,7 @@ public Task PolymorphicClassWithConstructor_TestData_Deserialization(Polymorphic => TestMultiContextDeserialization( testData.ExpectedJson, testData.ExpectedRoundtripValue, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); public static IEnumerable Get_PolymorphicClassWithConstructor_TestData_Deserialization() => PolymorphicClassWithConstructor.GetSerializeTestData() @@ -1171,7 +1162,7 @@ public async Task PolymorphicClassWithConstructor_TestDataArray_Deserialization( IEnumerable<(string ExpectedJson, PolymorphicClassWithConstructor ExpectedRoundtripValue)> inputs = PolymorphicClassWithConstructor.GetSerializeTestData().Select(entry => (entry.ExpectedJson, entry.ExpectedRoundtripValue)); - await TestMultiContextDeserialization(inputs, equalityComparer: PolymorphicEqualityComparer.Instance); + await TestMultiContextDeserialization(inputs, equalityComparer: CreateJsonEqualityComparer()); } [JsonPolymorphic] @@ -1300,7 +1291,7 @@ public Task PolymorphicInterface_TestData_Deserialization(PolymorphicInterface.T testData.ExpectedJson, testData.ExpectedRoundtripValue, testData.ExpectedDeserializationException, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); public static IEnumerable Get_PolymorphicInterface_TestData_Deserialization() => PolymorphicInterface.Helpers.GetSerializeTestData() @@ -1326,7 +1317,7 @@ public async Task PolymorphicInterface_TestDataArray_Deserialization() .Where(entry => entry.ExpectedRoundtripValue is not null) .Select(entry => (entry.ExpectedJson, entry.ExpectedRoundtripValue)); - await TestMultiContextDeserialization(inputs, equalityComparer: PolymorphicEqualityComparer.Instance); + await TestMultiContextDeserialization(inputs, equalityComparer: CreateJsonEqualityComparer()); } [Theory] @@ -1369,7 +1360,7 @@ public Task PolymorphicInterface_CustomConfigWithNearestAncestorFallback_TestDat testData.ExpectedRoundtripValue, testData.ExpectedDeserializationException, options: PolymorphicInterface.Helpers.CustomConfigWithNearestAncestorFallback, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); public static IEnumerable Get_PolymorphicInterface_CustomConfigWithNearestAncestorFallback_TestData_Deserialization() => PolymorphicInterface.Helpers.GetSerializeTestData_CustomConfigWithNearestAncestorFallback() @@ -1398,7 +1389,7 @@ public async Task PolymorphicInterface_CustomConfigWithNearestAncestorFallback_T await TestMultiContextDeserialization( inputs, options: PolymorphicInterface.Helpers.CustomConfigWithNearestAncestorFallback, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); } // -- @@ -1615,7 +1606,7 @@ public Task PolymorphicList_TestData_Deserialization(PolymorphicList.TestData te => TestMultiContextDeserialization( testData.ExpectedJson, testData.ExpectedRoundtripValue, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); public static IEnumerable Get_PolymorphicList_TestData_Deserialization() => PolymorphicList.GetSerializeTestData().Select(entry => new object[] { entry }); @@ -1635,7 +1626,7 @@ public async Task PolymorphicList_TestDataArray_Deserialization() IEnumerable<(string ExpectedJson, PolymorphicList ExpectedRoundtripValue)> inputs = PolymorphicList.GetSerializeTestData().Select(entry => (entry.ExpectedJson, entry.ExpectedRoundtripValue)); - await TestMultiContextDeserialization(inputs, equalityComparer: PolymorphicEqualityComparer.Instance); + await TestMultiContextDeserialization(inputs, equalityComparer: CreateJsonEqualityComparer()); } [Fact] @@ -1865,7 +1856,7 @@ public Task PolymorphicDictionary_TestData_Deserialization(PolymorphicDictionary => TestMultiContextDeserialization( testData.ExpectedJson, testData.ExpectedRoundtripValue, - equalityComparer: PolymorphicEqualityComparer.Instance); + equalityComparer: CreateJsonEqualityComparer()); public static IEnumerable Get_PolymorphicDictionary_TestData_Deserialization() => PolymorphicDictionary.GetSerializeTestData().Select(entry => new object[] { entry }); @@ -1885,7 +1876,7 @@ public async Task PolymorphicDictionary_TestDataArray_Deserialization() IEnumerable<(string ExpectedJson, PolymorphicDictionary ExpectedRoundtripValue)> inputs = PolymorphicDictionary.GetSerializeTestData().Select(entry => (entry.ExpectedJson, entry.ExpectedRoundtripValue)); - await TestMultiContextDeserialization(inputs, equalityComparer: PolymorphicEqualityComparer.Instance); + await TestMultiContextDeserialization(inputs, equalityComparer: CreateJsonEqualityComparer()); } [Fact] @@ -1944,7 +1935,7 @@ public static IEnumerable GetSerializeTestData() public record TestData(PolymorphicDictionary Value, string ExpectedJson, PolymorphicDictionary ExpectedRoundtripValue); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Fact] public async Task PolymorphicDictionaryInterface_Serialization() { var values = new IEnumerable>[] @@ -1968,7 +1959,7 @@ public async Task PolymorphicDictionaryInterface_Serialization() JsonTestHelper.AssertJsonEqual(expectedJson, actualJson); } - [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotNativeAot))] + [Fact] public async Task PolymorphicDictionaryInterface_Deserialization() { string json = @@ -2130,7 +2121,7 @@ public async Task ReferencePreservation_SingleValue_Deserialization(PolymorphicC { string json = jsonTemplate("1"); // root values have reference id "1" PolymorphicClass actualValue = await Serializer.DeserializeWrapper(json, s_jsonSerializerOptionsPreserveRefs); - Assert.Equal(expectedValue, actualValue, PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValue, actualValue, CreateJsonEqualityComparer()); } [Theory] @@ -2163,7 +2154,7 @@ public async Task ReferencePreservation_RepeatingValue_Deserialization(Polymorph var result = await Serializer.DeserializeWrapper>(json, s_jsonSerializerOptionsPreserveRefs); Assert.Equal(2, result.Count); - Assert.Equal(expectedValue, result[0], PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValue, result[0], CreateJsonEqualityComparer()); Assert.Same(result[0], result[1]); } @@ -2192,7 +2183,7 @@ public async Task ReferencePreservation_MultipleRepeatingValues_Deserialization( string json = "[" + string.Join(", ", idValues.Concat(refValues)) + "]"; PolymorphicClass[] result = await Serializer.DeserializeWrapper(json, s_jsonSerializerOptionsPreserveRefs); - Assert.Equal(expectedValues, result, PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValues, result, CreateJsonEqualityComparer()); } public static IEnumerable<(PolymorphicClass Value, Func JsonTemplate)> Get_ReferencePreservation_TestData() @@ -2232,7 +2223,7 @@ public async Task ReferencePreservation_CustomTypeDiscriminator_SingleValue_Dese { string json = jsonTemplate("1"); // root values have reference id "1" PolymorphicClassWithCustomTypeDiscriminator actualValue = await Serializer.DeserializeWrapper(json, s_jsonSerializerOptionsPreserveRefs); - Assert.Equal(expectedValue, actualValue, PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValue, actualValue, CreateJsonEqualityComparer()); } [Theory] @@ -2265,7 +2256,7 @@ public async Task ReferencePreservation_CustomTypeDiscriminator_RepeatingValue_D var result = await Serializer.DeserializeWrapper>(json, s_jsonSerializerOptionsPreserveRefs); Assert.Equal(2, result.Count); - Assert.Equal(expectedValue, result[0], PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValue, result[0], CreateJsonEqualityComparer()); Assert.Same(result[0], result[1]); } @@ -2294,7 +2285,7 @@ public async Task ReferencePreservation_CustomTypeDiscriminator_MultipleRepeatin string json = "[" + string.Join(", ", idValues.Concat(refValues)) + "]"; PolymorphicClassWithCustomTypeDiscriminator[] result = await Serializer.DeserializeWrapper(json, s_jsonSerializerOptionsPreserveRefs); - Assert.Equal(expectedValues, result, PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValues, result, CreateJsonEqualityComparer()); } [Theory] @@ -2303,7 +2294,7 @@ public async Task ReferencePreservation_AllowOutOfOrderMetadata_SingleValue_Dese { string json = jsonTemplate("1"); // root values have reference id "1" PolymorphicClass actualValue = await Serializer.DeserializeWrapper(json, s_jsonSerializerOptionsPreserveRefsAndAllowReadAhead); - Assert.Equal(expectedValue, actualValue, PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValue, actualValue, CreateJsonEqualityComparer()); } [Theory] @@ -2320,7 +2311,7 @@ public async Task ReferencePreservation_AllowOutOfOrderMetadata_RepeatingValue_D var result = await Serializer.DeserializeWrapper>(json, s_jsonSerializerOptionsPreserveRefsAndAllowReadAhead); Assert.Equal(2, result.Count); - Assert.Equal(expectedValue, result[0], PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValue, result[0], CreateJsonEqualityComparer()); Assert.Same(result[0], result[1]); } @@ -2335,7 +2326,7 @@ public async Task ReferencePreservation_AllowOutOfOrderMetadata_MultipleRepeatin string json = "[" + string.Join(", ", idValues.Concat(refValues)) + "]"; PolymorphicClass[] result = await Serializer.DeserializeWrapper(json, s_jsonSerializerOptionsPreserveRefsAndAllowReadAhead); - Assert.Equal(expectedValues, result, PolymorphicEqualityComparer.Instance); + Assert.Equal(expectedValues, result, CreateJsonEqualityComparer()); } [Theory] @@ -2947,7 +2938,14 @@ public class OpenGenericDerived_PartiallyConcrete : OpenGenericBase_Partially public T? Extra { get; set; } } - [Fact] + // Validates the runtime programmatic API by adding polymorphism to OpenGenericBase_Programmatic + // via a resolver modifier. The type is deliberately not registered with a JsonSerializerContext + // (no [JsonDerivedType]), so it relies on the reflection-based DefaultJsonTypeInfoResolver and only + // runs where runtime code generation is available; OpenGenericDerivedType_MixedWithRegularDerivedType_Works + // covers the attribute-based equivalent that also runs under source-gen. + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] + [RequiresUnreferencedCode("Uses DefaultJsonTypeInfoResolver and reflection-based serialization.")] + [RequiresDynamicCode("Uses DefaultJsonTypeInfoResolver and reflection-based serialization.")] public async Task OpenGenericDerivedType_ProgrammaticApi_Works() { var options = new JsonSerializerOptions @@ -3471,22 +3469,13 @@ public async Task Variance_CovariantInterface_VarianceOnlyAssignment_FallBackToB // Same as above with explicit FallBackToBaseType. The resolver falls through to the // base contract (no discriminator emitted) because the runtime type is not registered. // This matches the previously-described "serializes as base contract" behavior. - var options = new JsonSerializerOptions + JsonSerializerOptions options = Serializer.GetDefaultOptionsWithMetadataModifier(typeInfo => { - TypeInfoResolver = new DefaultJsonTypeInfoResolver + if (typeInfo.Type == typeof(IVarCovBase) && typeInfo.PolymorphismOptions is { } pOpts) { - Modifiers = - { - typeInfo => - { - if (typeInfo.Type == typeof(IVarCovBase) && typeInfo.PolymorphismOptions is { } pOpts) - { - pOpts.UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType; - } - } - } + pOpts.UnknownDerivedTypeHandling = JsonUnknownDerivedTypeHandling.FallBackToBaseType; } - }; + }); IVarCovBase value = new VarCovImpl { Value = new VarDog { Name = "Rex", Breed = "Labrador" } }; string json = await Serializer.SerializeWrapper(value, options); @@ -3798,51 +3787,14 @@ public class Derived : PolymorphicClassWithEscapedTypeDiscriminator; #endregion #region Test Helpers - public class PolymorphicEqualityComparer : IEqualityComparer - where TBaseType : class - { - public static PolymorphicEqualityComparer Instance { get; } = new(); - - public bool Equals(TBaseType? left, TBaseType? right) - { - if (left is null || right is null) - { - return left is null == right is null; - } - - Type runtimeType = left.GetType(); - if (runtimeType != right.GetType()) - { - return false; - } - - EqualityComparer objComparer = EqualityComparer.Default; - - // Runtime type is enumerable; use enumerable sequence comparison - if (left is IEnumerable leftColl) - { - IEnumerable rightColl = (IEnumerable)right; - return leftColl.Cast().SequenceEqual(rightColl.Cast(), objComparer); - } - - // Runtime is regular POCO; use property structural comparison - foreach (var propInfo in runtimeType.GetProperties()) - { - if (!objComparer.Equals(propInfo.GetValue(left), propInfo.GetValue(right))) - { - return false; - } - } - - return true; - } - - public int GetHashCode(TBaseType _) => throw new NotImplementedException(); - } public class CustomPolymorphismResolver : IJsonTypeInfoResolver { + #if BUILDING_SOURCE_GENERATOR_TESTS + private readonly IJsonTypeInfoResolver _inner = System.Text.Json.SourceGeneration.Tests.PolymorphicTests_Metadata.PolymorphicTestsContext_Metadata.Default; + #else private readonly DefaultJsonTypeInfoResolver _inner = new(); + #endif private readonly List _jsonDerivedTypes = new(); public CustomPolymorphismResolver(Type baseType) diff --git a/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.TypeClassifier.cs b/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.TypeClassifier.cs index 19b791b3c3665e..14d386fce2368a 100644 --- a/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.TypeClassifier.cs +++ b/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.TypeClassifier.cs @@ -649,7 +649,7 @@ public async Task Classifier_DiscriminatorBased_ResolvesCorrectType(string json, new JsonDerivedType(typeof(ClassifiedCat), "cat"), new JsonDerivedType(typeof(ClassifiedParrot), "parrot"),}, "kind"); - JsonTypeClassifier classify = factory.CreateJsonClassifier(context, new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }); + JsonTypeClassifier classify = factory.CreateJsonClassifier(context, Serializer.DefaultOptions); var options = CreateOptionsWithClassifier(classify); @@ -670,7 +670,7 @@ public async Task Classifier_DiscriminatorBased_AnyPropertyPosition(string json) new JsonDerivedType(typeof(ClassifiedDog), "dog"), new JsonDerivedType(typeof(ClassifiedCat), "cat"),}, "kind"); - JsonTypeClassifier classify = factory.CreateJsonClassifier(context, new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }); + JsonTypeClassifier classify = factory.CreateJsonClassifier(context, Serializer.DefaultOptions); var options = CreateOptionsWithClassifier(classify); @@ -692,7 +692,7 @@ public async Task Classifier_IntDiscriminator_ResolvesCorrectType(string json, T new JsonDerivedType(typeof(ClassifiedDog), 1), new JsonDerivedType(typeof(ClassifiedCat), 2),}, "type_id"); - JsonTypeClassifier classify = factory.CreateJsonClassifier(context, new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }); + JsonTypeClassifier classify = factory.CreateJsonClassifier(context, Serializer.DefaultOptions); var options = CreateOptionsWithClassifier(classify); @@ -996,7 +996,7 @@ public async Task Classifier_CollectionWithMixedDiscriminators() new JsonDerivedType(typeof(ClassifiedDog), "dog"), new JsonDerivedType(typeof(ClassifiedCat), "cat"),}, "kind"); - JsonTypeClassifier classify = factory.CreateJsonClassifier(context, new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }); + JsonTypeClassifier classify = factory.CreateJsonClassifier(context, Serializer.DefaultOptions); var options = CreateOptionsWithClassifier(classify); string json = """[{"kind":"dog","Name":"Rex","Breed":"Lab"},{"kind":"cat","Name":"Whiskers","Lives":9}]"""; @@ -1115,7 +1115,7 @@ public async Task Classifier_PlusAllowOutOfOrder_ClassifierWins() new JsonDerivedType(typeof(ClassifiedDog), "dog"), new JsonDerivedType(typeof(ClassifiedCat), "cat"),}, "kind"); - JsonTypeClassifier classify = factory.CreateJsonClassifier(context, new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }); + JsonTypeClassifier classify = factory.CreateJsonClassifier(context, Serializer.DefaultOptions); JsonSerializerOptions options = Serializer.GetDefaultOptionsWithMetadataModifier(typeInfo => { @@ -1239,7 +1239,7 @@ public async Task Classifier_DifferentClassifiersForDifferentBaseTypes() new JsonDerivedType(typeof(ClassifiedDog), "dog"), new JsonDerivedType(typeof(ClassifiedCat), "cat"),}, "kind"); - JsonTypeClassifier animalClassify = animalFactory.CreateJsonClassifier(animalContext, new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }); + JsonTypeClassifier animalClassify = animalFactory.CreateJsonClassifier(animalContext, Serializer.DefaultOptions); JsonSerializerOptions options = Serializer.GetDefaultOptionsWithMetadataModifier(typeInfo => { @@ -1462,7 +1462,7 @@ public async Task Classifier_WithReferenceHandlerPreserve_PreservesReferences() new JsonDerivedType(typeof(ClassifiedDog), "dog"), new JsonDerivedType(typeof(ClassifiedCat), "cat"),}, "kind"); - JsonTypeClassifier classify = factory.CreateJsonClassifier(context, new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }); + JsonTypeClassifier classify = factory.CreateJsonClassifier(context, Serializer.DefaultOptions); JsonSerializerOptions options = Serializer.GetDefaultOptionsWithMetadataModifier(typeInfo => { diff --git a/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.cs b/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.cs index 06aa89d9332f95..6854c486b607f0 100644 --- a/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/PolymorphicTests.cs @@ -7,6 +7,7 @@ using System.Text.Json.Nodes; using System.Text.Json.Serialization.Metadata; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; using Microsoft.DotNet.XUnitExtensions; using Xunit; @@ -33,8 +34,7 @@ public async Task PrimitivesAsRootObject(object? value, string expectedJson) json = await Serializer.SerializeWrapper(value, typeof(object)); Assert.Equal(expectedJson, json); - var options = new JsonSerializerOptions { TypeInfoResolver = new DefaultJsonTypeInfoResolver() }; - JsonTypeInfo objectTypeInfo = options.GetTypeInfo(); + JsonTypeInfo objectTypeInfo = Serializer.DefaultOptions.GetTypeInfo(); json = await Serializer.SerializeWrapper(value, objectTypeInfo); Assert.Equal(expectedJson, json); } @@ -573,23 +573,14 @@ public async Task AnonymousType() [Fact] public async Task CustomResolverWithFailingAncestorType_DoesNotSurfaceException() { - var options = new JsonSerializerOptions + JsonSerializerOptions options = Serializer.GetDefaultOptionsWithMetadataModifier(static typeInfo => { - TypeInfoResolver = new DefaultJsonTypeInfoResolver + if (typeInfo.Type == typeof(MyThing) || + typeInfo.Type == typeof(IList)) { - Modifiers = - { - static typeInfo => - { - if (typeInfo.Type == typeof(MyThing) || - typeInfo.Type == typeof(IList)) - { - throw new InvalidOperationException("some latent custom resolution bug"); - } - } - } + throw new InvalidOperationException("some latent custom resolution bug"); } - }; + }); object value = new MyDerivedThing { Number = 42 }; string json = await Serializer.SerializeWrapper(value, options); @@ -627,7 +618,7 @@ internal class MyThingDictionary : Dictionary { } [Theory] [InlineData(typeof(PolymorphicTypeWithConflictingPropertyNameAtBase), typeof(PolymorphicTypeWithConflictingPropertyNameAtBase.Derived))] [InlineData(typeof(PolymorphicTypeWithConflictingPropertyNameAtDerived), typeof(PolymorphicTypeWithConflictingPropertyNameAtDerived.Derived))] - public async Task PolymorphicTypesWithConflictingPropertyNames_ThrowsInvalidOperationException(Type baseType, Type derivedType) + public async Task PolymorphicTypesWithConflictingPropertyNames_ThrowsInvalidOperationException(Type baseType, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type derivedType) { InvalidOperationException ex; object value = Activator.CreateInstance(derivedType); diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs index d3adb66f38f2f8..b9c0b51e3b66b5 100644 --- a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.InitOnly.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Xunit; @@ -11,7 +12,7 @@ public abstract partial class PropertyVisibilityTests [Theory] [InlineData(typeof(ClassWithInitOnlyProperty))] [InlineData(typeof(StructWithInitOnlyProperty))] - public virtual async Task InitOnlyProperties(Type type) + public virtual async Task InitOnlyProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // Init-only property included by default. object obj = await Serializer.DeserializeWrapper("""{"MyInt":1}""", type); @@ -24,7 +25,7 @@ public virtual async Task InitOnlyProperties(Type type) [Theory] [InlineData(typeof(ClassWithCustomNamedInitOnlyProperty))] [InlineData(typeof(StructWithCustomNamedInitOnlyProperty))] - public virtual async Task CustomNamedInitOnlyProperties(Type type) + public virtual async Task CustomNamedInitOnlyProperties([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // Regression test for https://github.com/dotnet/runtime/issues/82730 @@ -40,7 +41,7 @@ public virtual async Task CustomNamedInitOnlyProperties(Type type) [InlineData(typeof(Class_PropertyWith_PrivateInitOnlySetter))] [InlineData(typeof(Class_PropertyWith_InternalInitOnlySetter))] [InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter))] - public async Task NonPublicInitOnlySetter_Without_JsonInclude_Fails(Type type) + public async Task NonPublicInitOnlySetter_Without_JsonInclude_Fails([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // Non-public init-only property setter ignored. object obj = await Serializer.DeserializeWrapper("""{"MyInt":1}""", type); @@ -54,7 +55,7 @@ public async Task NonPublicInitOnlySetter_Without_JsonInclude_Fails(Type type) [InlineData(typeof(Class_PropertyWith_PrivateInitOnlySetter_WithAttribute))] [InlineData(typeof(Class_PropertyWith_InternalInitOnlySetter_WithAttribute))] [InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter_WithAttribute))] - public virtual async Task NonPublicInitOnlySetter_With_JsonInclude(Type type) + public virtual async Task NonPublicInitOnlySetter_With_JsonInclude([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { // Non-public init-only property setter included with [JsonInclude]. object obj = await Serializer.DeserializeWrapper("""{"MyInt":1}""", type); @@ -67,7 +68,7 @@ public virtual async Task NonPublicInitOnlySetter_With_JsonInclude(Type type) [Theory] [InlineData(typeof(Class_WithIgnoredInitOnlyProperty))] [InlineData(typeof(Record_WithIgnoredPropertyInCtor))] - public async Task InitOnlySetter_With_JsonIgnoreAlways(Type type) + public async Task InitOnlySetter_With_JsonIgnoreAlways([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { object obj = await Serializer.DeserializeWrapper("""{"MyInt":42}""", type); Assert.Equal(0, (int)type.GetProperty("MyInt").GetValue(obj)); @@ -196,7 +197,7 @@ public record InnerRecord(int Foo, string Bar); [Theory] [InlineData(typeof(ClassWithInitOnlyPropertyDefaults))] [InlineData(typeof(StructWithInitOnlyPropertyDefaults))] - public async Task InitOnlyPropertyDefaultValues_PreservedWhenMissingFromJson(Type type) + public async Task InitOnlyPropertyDefaultValues_PreservedWhenMissingFromJson([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // When no properties are present in JSON, C# default values should be preserved. object obj = await Serializer.DeserializeWrapper("{}", type); @@ -207,7 +208,7 @@ public async Task InitOnlyPropertyDefaultValues_PreservedWhenMissingFromJson(Typ [Theory] [InlineData(typeof(ClassWithInitOnlyPropertyDefaults))] [InlineData(typeof(StructWithInitOnlyPropertyDefaults))] - public async Task InitOnlyPropertyDefaultValues_OverriddenWhenPresentInJson(Type type) + public async Task InitOnlyPropertyDefaultValues_OverriddenWhenPresentInJson([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // When properties are present in JSON, specified values should be used. object obj = await Serializer.DeserializeWrapper("""{"Name":"Override","Number":99}""", type); @@ -218,7 +219,7 @@ public async Task InitOnlyPropertyDefaultValues_OverriddenWhenPresentInJson(Type [Theory] [InlineData(typeof(ClassWithInitOnlyPropertyDefaults))] [InlineData(typeof(StructWithInitOnlyPropertyDefaults))] - public async Task InitOnlyPropertyDefaultValues_PartialOverride(Type type) + public async Task InitOnlyPropertyDefaultValues_PartialOverride([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // When only some properties are present in JSON, the rest should keep defaults. object obj = await Serializer.DeserializeWrapper("""{"Number":99}""", type); diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs index 839156f7b98bbb..bc903071112aaa 100644 --- a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.NonPublicAccessors.cs @@ -1,7 +1,8 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text.Json.Serialization.Metadata; @@ -169,7 +170,7 @@ private class ClassWithExtensionData_NonPublicGetter public virtual async Task HonorCustomConverter_UsingPrivateSetter() { var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); + options.Converters.Add(new JsonStringEnumConverter()); string json = """{"MyEnum":"AnotherValue","MyInt":2}"""; @@ -360,7 +361,7 @@ public class ClassWithMixedPropertyAccessors_PropertyAttributes [InlineData(typeof(ClassWithPrivate_InitOnlyProperty_WithJsonIncludeProperty), false)] [InlineData(typeof(ClassWithInternal_InitOnlyProperty_WithJsonIncludeProperty), true)] [InlineData(typeof(ClassWithProtected_InitOnlyProperty_WithJsonIncludeProperty), false)] - public virtual async Task NonPublicProperty_JsonInclude_WorksAsExpected(Type type, bool isAccessibleBySourceGen) + public virtual async Task NonPublicProperty_JsonInclude_WorksAsExpected([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, bool isAccessibleBySourceGen) { if (!Serializer.IsSourceGeneratedSerializer || isAccessibleBySourceGen) { diff --git a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs index 3a5daf6e5d1182..2dbbb5325051d5 100644 --- a/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/PropertyVisibilityTests.cs @@ -1,9 +1,10 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Numerics; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -1785,7 +1786,7 @@ public async Task OverrideJsonIgnorePropertyUsingJsonPropertyNameReversed() [Theory] [InlineData(typeof(ClassWithProperty_IgnoreConditionAlways))] [InlineData(typeof(ClassWithProperty_IgnoreConditionAlways_Ctor))] - public async Task JsonIgnoreConditionSetToAlwaysWorks(Type type) + public async Task JsonIgnoreConditionSetToAlwaysWorks([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { string json = """{"MyString":"Random","MyDateTime":"2020-03-23","MyInt":4}"""; @@ -1830,7 +1831,7 @@ public ClassWithProperty_IgnoreConditionAlways_Ctor(DateTime myDateTime, int myI [Theory] [MemberData(nameof(JsonIgnoreConditionWhenWritingDefault_ClassProperty_TestData))] - public async Task JsonIgnoreConditionWhenWritingDefault_ClassProperty(Type type, JsonSerializerOptions options) + public async Task JsonIgnoreConditionWhenWritingDefault_ClassProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, JsonSerializerOptions options) { // Property shouldn't be ignored if it isn't null. string json = """{"Int1":1,"MyString":"Random","Int2":2}"""; @@ -1916,7 +1917,7 @@ public static IEnumerable JsonIgnoreConditionWhenWritingDefault_ClassP [Theory] [MemberData(nameof(JsonIgnoreConditionWhenWritingDefault_StructProperty_TestData))] - public async Task JsonIgnoreConditionWhenWritingDefault_StructProperty(Type type, JsonSerializerOptions options) + public async Task JsonIgnoreConditionWhenWritingDefault_StructProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, JsonSerializerOptions options) { // Property shouldn't be ignored if it isn't null. string json = """{"Int1":1,"MyInt":3,"Int2":2}"""; @@ -1974,7 +1975,7 @@ public static IEnumerable JsonIgnoreConditionWhenWritingDefault_Struct [Theory] [MemberData(nameof(JsonIgnoreConditionNever_TestData))] - public async Task JsonIgnoreConditionNever(Type type) + public async Task JsonIgnoreConditionNever([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // Property should always be (de)serialized, even when null. string json = """{"Int1":1,"MyString":"Random","Int2":2}"""; @@ -2017,7 +2018,7 @@ public async Task JsonIgnoreConditionNever(Type type) [Theory] [MemberData(nameof(JsonIgnoreConditionNever_TestData))] - public async Task JsonIgnoreConditionNever_IgnoreNullValues_True(Type type) + public async Task JsonIgnoreConditionNever_IgnoreNullValues_True([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { // Property should always be (de)serialized. string json = """{"Int1":1,"MyString":"Random","Int2":2}"""; @@ -2903,7 +2904,7 @@ public class ClassWithThingsToIgnore_PerProperty [Theory] [InlineData(typeof(ClassWithBadIgnoreAttribute))] [InlineData(typeof(StructWithBadIgnoreAttribute))] - public virtual async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail(Type type) + public virtual async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { InvalidOperationException ex = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper("{}", type)); string exAsStr = ex.ToString(); @@ -2923,7 +2924,7 @@ public virtual async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail(T [Theory] [InlineData(typeof(ClassWithBadIgnoreAttribute))] [InlineData(typeof(StructWithBadIgnoreAttribute))] - public virtual async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson(Type type) + public virtual async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { InvalidOperationException ex = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper("", type)); string exAsStr = ex.ToString(); diff --git a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.IgnoreCycles.cs b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.IgnoreCycles.cs index 0745c8d78e6adf..24f865a347d323 100644 --- a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.IgnoreCycles.cs +++ b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.IgnoreCycles.cs @@ -8,6 +8,7 @@ using System.Diagnostics; using System.IO; using System.Threading.Tasks; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -28,7 +29,7 @@ public async Task IgnoreCycles_OnObject() await Verify(); await Verify(); - async Task Verify() where T : class, new() + async Task Verify<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>() where T : class, new() { T root = new T(); SetNextProperty(typeof(T), root, root); @@ -48,7 +49,7 @@ public async Task IgnoreCycles_OnObject_AsProperty() await Verify(); await Verify(); - async Task Verify() where T : class, new() + async Task Verify<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>() where T : class, new() { var node = new T(); SetNextProperty(typeof(T), node, node); @@ -77,7 +78,7 @@ public async Task IgnoreCycles_OnBoxedValueType() await Verify(); await Verify(); - async Task Verify() where T : new() + async Task Verify<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>() where T : new() { object root = new T(); SetNextProperty(typeof(T), root, root); @@ -107,7 +108,7 @@ public async Task IgnoreCycles_OnBoxedValueType_AsProperty() await Verify(); await Verify(); - async Task Verify() where T : new() + async Task Verify<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>() where T : new() { object node = new T(); SetNextProperty(typeof(T), node, node); @@ -125,7 +126,7 @@ public async Task IgnoreCycles_OnBoxedValueType_AsProperty() [Theory] [InlineData(typeof(Dictionary))] [InlineData(typeof(GenericIDictionaryWrapper))] - public async Task IgnoreCycles_OnDictionary(Type typeToSerialize) + public async Task IgnoreCycles_OnDictionary([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type typeToSerialize) { var root = (IDictionary)Activator.CreateInstance(typeToSerialize); root.Add("self", root); @@ -172,7 +173,7 @@ public async Task IgnoreCycles_OnArray() [Theory] [InlineData(typeof(List))] [InlineData(typeof(GenericIListWrapper))] - public async Task IgnoreCycles_OnList(Type typeToSerialize) + public async Task IgnoreCycles_OnList([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type typeToSerialize) { var root = (IList)Activator.CreateInstance(typeToSerialize); root.Add(root); @@ -190,7 +191,7 @@ public async Task IgnoreCycles_OnRecursiveList() [Theory] [InlineData(typeof(GenericISetWrapper))] [InlineData(typeof(GenericICollectionWrapper))] - public async Task IgnoreCycles_OnCollections(Type typeToSerialize) + public async Task IgnoreCycles_OnCollections([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type typeToSerialize) { var root = (ICollection)Activator.CreateInstance(typeToSerialize); root.Add(root); @@ -531,12 +532,12 @@ static void VerifySubstringExistsNTimes(string actualString, string expectedSubs } private const string Next = nameof(Next); - private void SetNextProperty(Type type, object obj, object value) + private void SetNextProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, object obj, object value) { type.GetProperty(Next).SetValue(obj, value); } - private object GetNextProperty(Type type, object obj) + private object GetNextProperty([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type, object obj) { return type.GetProperty(Next).GetValue(obj); } diff --git a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.Serialize.cs b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.Serialize.cs index 61ee63d2906f5d..8c66d4f852374e 100644 --- a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.Serialize.cs +++ b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.Serialize.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Newtonsoft.Json; using Xunit; @@ -14,6 +15,25 @@ public abstract partial class ReferenceHandlerTests : SerializerTests private static readonly JsonSerializerOptions s_serializerOptionsPreserve = new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve }; private static readonly JsonSerializerSettings s_newtonsoftSerializerSettingsPreserve = new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.All, ReferenceLoopHandling = ReferenceLoopHandling.Serialize }; + // Asserts that the serializer output matches the Newtonsoft.Json reference-preservation baseline. + // The Newtonsoft.Json baseline is reflection-based (its APIs are annotated RequiresUnreferencedCode + // and RequiresDynamicCode), so it is only exercised when dynamic code is supported. The runtime guard + // makes the call unreachable under Native AOT (where PlatformDetection.IsReflectionEmitSupported is + // false), while still running the baseline in the non-AOT source generator configuration. The trim/AOT + // analyzers cannot see through the guard, so the warnings are suppressed here. + [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", + Justification = "The Newtonsoft.Json baseline only runs when dynamic code is supported, guarded by PlatformDetection.IsReflectionEmitSupported.")] + [UnconditionalSuppressMessage("AOT", "IL3050:RequiresDynamicCode", + Justification = "The Newtonsoft.Json baseline only runs when dynamic code is supported, guarded by PlatformDetection.IsReflectionEmitSupported.")] + private static void AssertOutputEqualsNewtonsoft(object value, string actual) + { + if (PlatformDetection.IsReflectionEmitSupported) + { + string expected = JsonConvert.SerializeObject(value, s_newtonsoftSerializerSettingsPreserve); + Assert.Equal(expected, actual); + } + } + public class Employee { public string? Name { get; set; } @@ -52,10 +72,9 @@ public async Task ExtensionDataDictionaryHandlesPreserveReferences() angela.ExtensionData = extensionData; - string expected = JsonConvert.SerializeObject(angela, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(angela, s_serializerOptionsPreserve); - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(angela, actual); } #region struct tests diff --git a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.cs b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.cs index ad23a6f3af3916..f09fd6d0fb3f44 100644 --- a/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/ReferenceHandlerTests/ReferenceHandlerTests.cs @@ -7,7 +7,7 @@ using System.Text.Encodings.Web; using System.Text.Json.Nodes; using System.Threading.Tasks; -using Newtonsoft.Json; +using System.Diagnostics.CodeAnalysis; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -34,11 +34,8 @@ public async Task ObjectLoop() Employee angela = new Employee(); angela.Manager = angela; - // Compare parity with Newtonsoft.Json - string expected = JsonConvert.SerializeObject(angela, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(angela, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(angela, actual); // Ensure round-trip Employee angelaCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); @@ -51,10 +48,8 @@ public async Task ObjectArrayLoop() Employee angela = new Employee(); angela.Subordinates = new List { angela }; - string expected = JsonConvert.SerializeObject(angela, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(angela, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(angela, actual); Employee angelaCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(angelaCopy.Subordinates[0], angelaCopy); @@ -66,10 +61,8 @@ public async Task ObjectDictionaryLoop() Employee angela = new Employee(); angela.Contacts = new Dictionary { { "555-5555", angela } }; - string expected = JsonConvert.SerializeObject(angela, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(angela, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(angela, actual); Employee angelaCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(angelaCopy.Contacts["555-5555"], angelaCopy); @@ -84,10 +77,8 @@ public async Task ObjectPreserveDuplicateObjects() }; angela.Manager2 = angela.Manager; - string expected = JsonConvert.SerializeObject(angela, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(angela, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(angela, actual); Employee angelaCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(angelaCopy.Manager, angelaCopy.Manager2); @@ -102,10 +93,8 @@ public async Task ObjectPreserveDuplicateDictionaries() }; angela.Contacts2 = angela.Contacts; - string expected = JsonConvert.SerializeObject(angela, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(angela, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(angela, actual); Employee angelaCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(angelaCopy.Contacts, angelaCopy.Contacts2); @@ -120,10 +109,8 @@ public async Task ObjectPreserveDuplicateArrays() }; angela.Subordinates2 = angela.Subordinates; - string expected = JsonConvert.SerializeObject(angela, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(angela, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(angela, actual); Employee angelaCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(angelaCopy.Subordinates, angelaCopy.Subordinates2); @@ -253,10 +240,8 @@ public async Task DictionaryLoop() root["Self"] = root; root["Other"] = new DictionaryWithGenericCycle(); - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); DictionaryWithGenericCycle rootCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy["Self"]); @@ -269,10 +254,8 @@ public async Task DictionaryPreserveDuplicateDictionaries() root["Self1"] = root; root["Self2"] = root; - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); DictionaryWithGenericCycle rootCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy["Self1"]); @@ -285,10 +268,8 @@ public async Task DictionaryObjectLoop() Dictionary root = new Dictionary(); root["Angela"] = new Employee() { Name = "Angela", Contacts = root }; - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); Dictionary rootCopy = await Serializer.DeserializeWrapper>(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy["Angela"].Contacts); @@ -302,10 +283,8 @@ public async Task DictionaryArrayLoop() DictionaryWithGenericCycleWithinList root = new DictionaryWithGenericCycleWithinList(); root["ArrayWithSelf"] = new List { root }; - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); DictionaryWithGenericCycleWithinList rootCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy["ArrayWithSelf"][0]); @@ -318,10 +297,8 @@ public async Task DictionaryPreserveDuplicateArrays() root["Array1"] = new List { root }; root["Array2"] = root["Array1"]; - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); DictionaryWithGenericCycleWithinList rootCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy["Array1"][0]); @@ -337,10 +314,8 @@ public async Task DictionaryPreserveDuplicateObjects() }; root["Employee2"] = root["Employee1"]; - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); Dictionary rootCopy = await Serializer.DeserializeWrapper>(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy["Employee1"], rootCopy["Employee2"]); @@ -447,10 +422,8 @@ public async Task ArrayLoop() ListWithGenericCycle root = new ListWithGenericCycle(); root.Add(root); - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); ListWithGenericCycle rootCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy[0]); @@ -461,10 +434,8 @@ public async Task ArrayLoop() root.Add(root); root.Add(root); - expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); rootCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy[0]); @@ -478,10 +449,8 @@ public async Task ArrayObjectLoop() List root = new List(); root.Add(new Employee() { Name = "Angela", Subordinates = root }); - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); List rootCopy = await Serializer.DeserializeWrapper>(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy[0].Subordinates); @@ -496,10 +465,8 @@ public async Task ArrayPreserveDuplicateObjects() }; root.Add(root[0]); - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); List rootCopy = await Serializer.DeserializeWrapper>(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy[0], rootCopy[1]); @@ -513,10 +480,8 @@ public async Task ArrayDictionaryLoop() ListWithGenericCycleWithinDictionary root = new ListWithGenericCycleWithinDictionary(); root.Add(new Dictionary { { "Root", root } }); - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); ListWithGenericCycleWithinDictionary rootCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy, rootCopy[0]["Root"]); @@ -531,10 +496,8 @@ public async Task ArrayPreserveDuplicateDictionaries() }; root.Add(root[0]); - string expected = JsonConvert.SerializeObject(root, s_newtonsoftSerializerSettingsPreserve); string actual = await Serializer.SerializeWrapper(root, s_serializerOptionsPreserve); - - Assert.Equal(expected, actual); + AssertOutputEqualsNewtonsoft(root, actual); ListWithGenericCycleWithinDictionary rootCopy = await Serializer.DeserializeWrapper(actual, s_serializerOptionsPreserve); Assert.Same(rootCopy[0], rootCopy[1]); @@ -864,7 +827,7 @@ public IEnumerator GetEnumerator() [Theory] [InlineData(typeof(ClassWithConflictingRefProperty), "$ref")] [InlineData(typeof(ClassWithConflictingIdProperty), "$id")] - public async Task ClassWithConflictingMetadataProperties_ThrowsInvalidOperationException(Type type, string propertyName) + public async Task ClassWithConflictingMetadataProperties_ThrowsInvalidOperationException([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type, string propertyName) { InvalidOperationException ex; object value = Activator.CreateInstance(type); diff --git a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs index 9b70145773653d..9b51ffef337d50 100644 --- a/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/RequiredKeywordTests.cs @@ -5,6 +5,7 @@ using System.Diagnostics.CodeAnalysis; using System.Text.Json.Serialization.Metadata; using System.Threading.Tasks; +using Microsoft.DotNet.XUnitExtensions; using Xunit; namespace System.Text.Json.Serialization.Tests @@ -490,7 +491,9 @@ public class ClassWithRequiredField public required int RequiredField; } - [Fact] + [ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsReflectionEmitSupported))] + [RequiresUnreferencedCode("Uses JsonTypeInfo.CreateJsonPropertyInfo which requires reflection.")] + [RequiresDynamicCode("Uses JsonTypeInfo.CreateJsonPropertyInfo which requires reflection.")] public async Task RemovingPropertiesWithRequiredKeywordAllowsDeserialization() { JsonSerializerOptions options = Serializer.GetDefaultOptionsWithMetadataModifier(static ti => @@ -746,12 +749,7 @@ public ClassWithNullValidatingConstructor(string value) public string Value { get; } } - private static JsonTypeInfo GetTypeInfo(JsonSerializerOptions options) - { - options.TypeInfoResolver ??= JsonSerializerOptions.Default.TypeInfoResolver; - options.MakeReadOnly(); - return options.GetTypeInfo(typeof(T)); - } + private JsonTypeInfo GetTypeInfo(JsonSerializerOptions options) => Serializer.GetTypeInfo(options); private static void AssertJsonTypeInfoHasRequiredProperties(JsonTypeInfo typeInfo, params string[] requiredProperties) { diff --git a/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs b/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs index 084a88aa32b6ed..4b79d2991f600a 100644 --- a/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/SerializerTests.cs @@ -249,7 +249,7 @@ protected async Task TestMultiContextDeserialization( string wrappedJson; equalityComparer ??= expectedValue is IEquatable ? EqualityComparer.Default - : new JsonEqualityComparer(); + : new JsonEqualityComparer(Serializer.GetTypeInfo(options)); if (contexts.HasFlag(SerializedValueContext.RootValue)) { @@ -366,10 +366,39 @@ internal class GenericPoco public T Property { get; set; } } + // Creates a trim/AOT-safe equality comparer that compares values by serializing them + // using the resolved (polymorphism-aware) JsonTypeInfo and comparing the resulting JSON. + // Values of differing runtime types are never considered equal, preserving the behavior of + // the reflection-based structural comparer this replaced (so that, for example, a nearest- + // ancestor fallback regression cannot slip through when a derived type happens to serialize + // identically under its base contract). + protected IEqualityComparer CreateJsonEqualityComparer(JsonSerializerOptions? options = null) + => new JsonEqualityComparer(Serializer.GetTypeInfo(options), compareRuntimeType: true); + private class JsonEqualityComparer : IEqualityComparer { - public bool Equals(TValue? x, TValue? y) => JsonSerializer.Serialize(x) == JsonSerializer.Serialize(y); - public int GetHashCode([DisallowNull] TValue obj) => JsonSerializer.Serialize(obj).GetHashCode(); + private readonly JsonTypeInfo _jsonTypeInfo; + private readonly bool _compareRuntimeType; + + public JsonEqualityComparer(JsonTypeInfo jsonTypeInfo, bool compareRuntimeType = false) + { + _jsonTypeInfo = jsonTypeInfo; + _compareRuntimeType = compareRuntimeType; + } + + public bool Equals(TValue? x, TValue? y) + { + // Object.GetType() is trim/AOT-safe and lets us reject differing runtime types before + // falling back to structural (serialized JSON) comparison. + if (_compareRuntimeType && x is not null && y is not null && x.GetType() != y.GetType()) + { + return false; + } + + return JsonSerializer.Serialize(x, _jsonTypeInfo) == JsonSerializer.Serialize(y, _jsonTypeInfo); + } + + public int GetHashCode([DisallowNull] TValue obj) => JsonSerializer.Serialize(obj, _jsonTypeInfo).GetHashCode(); } private class ListAssertionEqualityComparer : IEqualityComparer> diff --git a/src/libraries/System.Text.Json/tests/Common/StructuralJsonTypeClassifierTests.cs b/src/libraries/System.Text.Json/tests/Common/StructuralJsonTypeClassifierTests.cs index b1131020528f34..ab4de54ead4885 100644 --- a/src/libraries/System.Text.Json/tests/Common/StructuralJsonTypeClassifierTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/StructuralJsonTypeClassifierTests.cs @@ -13,31 +13,26 @@ namespace System.Text.Json.Serialization.Tests { public abstract class StructuralJsonTypeClassifierTests(JsonSerializerWrapper serializerUnderTest) : SerializerTests(serializerUnderTest) { - private static readonly JsonSerializerOptions s_options = new() + private readonly JsonSerializerOptions _options = new(serializerUnderTest.DefaultOptions); + private readonly JsonSerializerOptions _caseInsensitiveOptions = new(serializerUnderTest.DefaultOptions) { - TypeInfoResolver = new DefaultJsonTypeInfoResolver() + PropertyNameCaseInsensitive = true }; - private static readonly JsonSerializerOptions s_caseInsensitiveOptions = new() + private readonly JsonSerializerOptions _numberFromStringOptions = new(serializerUnderTest.DefaultOptions) { - PropertyNameCaseInsensitive = true, - TypeInfoResolver = new DefaultJsonTypeInfoResolver() - }; - private static readonly JsonSerializerOptions s_numberFromStringOptions = new() - { - NumberHandling = JsonNumberHandling.AllowReadingFromString, - TypeInfoResolver = new DefaultJsonTypeInfoResolver() + NumberHandling = JsonNumberHandling.AllowReadingFromString }; [Fact] public async Task StructuralClassifier_DistinguishesObjectProperties() { - PetUnion? dog = await Serializer.DeserializeWrapper("""{"Name":"Rex","Breed":"Labrador"}""", s_options); + PetUnion? dog = await Serializer.DeserializeWrapper("""{"Name":"Rex","Breed":"Labrador"}""", _options); Assert.NotNull(dog); Dog dogValue = Assert.IsType(GetUnionValue(dog)); Assert.Equal("Rex", dogValue.Name); Assert.Equal("Labrador", dogValue.Breed); - PetUnion? cat = await Serializer.DeserializeWrapper("""{"Name":"Misty","Lives":9}""", s_options); + PetUnion? cat = await Serializer.DeserializeWrapper("""{"Name":"Misty","Lives":9}""", _options); Assert.NotNull(cat); Cat catValue = Assert.IsType(GetUnionValue(cat)); Assert.Equal("Misty", catValue.Name); @@ -47,15 +42,15 @@ public async Task StructuralClassifier_DistinguishesObjectProperties() [Fact] public async Task StructuralClassifier_DistinguishesStringPatterns() { - TemporalUnion? date = await Serializer.DeserializeWrapper("\"2026-05-11T18:00:00Z\"", s_options); + TemporalUnion? date = await Serializer.DeserializeWrapper("\"2026-05-11T18:00:00Z\"", _options); Assert.NotNull(date); Assert.IsType(GetUnionValue(date)); - TemporalUnion? duration = await Serializer.DeserializeWrapper("\"01:02:03\"", s_options); + TemporalUnion? duration = await Serializer.DeserializeWrapper("\"01:02:03\"", _options); Assert.NotNull(duration); Assert.Equal(TimeSpan.FromSeconds(3723), Assert.IsType(GetUnionValue(duration))); - TemporalUnion? guid = await Serializer.DeserializeWrapper("\"0f8fad5b-d9cb-469f-a165-70867728950e\"", s_options); + TemporalUnion? guid = await Serializer.DeserializeWrapper("\"0f8fad5b-d9cb-469f-a165-70867728950e\"", _options); Assert.NotNull(guid); Assert.Equal(new Guid("0f8fad5b-d9cb-469f-a165-70867728950e"), Assert.IsType(GetUnionValue(guid))); } @@ -63,11 +58,11 @@ public async Task StructuralClassifier_DistinguishesStringPatterns() [Fact] public async Task StructuralClassifier_SamplesCollectionElementShapes() { - ListUnion? numbers = await Serializer.DeserializeWrapper("[1,2,3]", s_options); + ListUnion? numbers = await Serializer.DeserializeWrapper("[1,2,3]", _options); Assert.NotNull(numbers); Assert.Equal([1, 2, 3], Assert.IsType>(GetUnionValue(numbers))); - ListUnion? strings = await Serializer.DeserializeWrapper("""["one","two"]""", s_options); + ListUnion? strings = await Serializer.DeserializeWrapper("""["one","two"]""", _options); Assert.NotNull(strings); Assert.Equal(["one", "two"], Assert.IsType>(GetUnionValue(strings))); } @@ -75,11 +70,11 @@ public async Task StructuralClassifier_SamplesCollectionElementShapes() [Fact] public async Task StructuralClassifier_SamplesDictionaryValueShapes() { - DictionaryUnion? numbers = await Serializer.DeserializeWrapper("""{"one":1,"two":2}""", s_options); + DictionaryUnion? numbers = await Serializer.DeserializeWrapper("""{"one":1,"two":2}""", _options); Assert.NotNull(numbers); Assert.Equal(2, Assert.IsType>(GetUnionValue(numbers))["two"]); - DictionaryUnion? strings = await Serializer.DeserializeWrapper("""{"one":"uno","two":"dos"}""", s_options); + DictionaryUnion? strings = await Serializer.DeserializeWrapper("""{"one":"uno","two":"dos"}""", _options); Assert.NotNull(strings); Assert.Equal("dos", Assert.IsType>(GetUnionValue(strings))["two"]); } @@ -87,12 +82,12 @@ public async Task StructuralClassifier_SamplesDictionaryValueShapes() [Fact] public async Task StructuralClassifier_RecursesIntoNestedGenericShapes() { - BatchUnion? temperatures = await Serializer.DeserializeWrapper("""{"Source":"sensor","Items":[{"Celsius":21.5}]}""", s_options); + BatchUnion? temperatures = await Serializer.DeserializeWrapper("""{"Source":"sensor","Items":[{"Celsius":21.5}]}""", _options); Assert.NotNull(temperatures); Batch temperatureBatch = Assert.IsType>(GetUnionValue(temperatures)); Assert.Equal(21.5, temperatureBatch.Items![0].Celsius); - BatchUnion? statuses = await Serializer.DeserializeWrapper("""{"Source":"sensor","Items":[{"IsOnline":true}]}""", s_options); + BatchUnion? statuses = await Serializer.DeserializeWrapper("""{"Source":"sensor","Items":[{"IsOnline":true}]}""", _options); Assert.NotNull(statuses); Batch statusBatch = Assert.IsType>(GetUnionValue(statuses)); Assert.True(statusBatch.Items![0].IsOnline); @@ -101,11 +96,11 @@ public async Task StructuralClassifier_RecursesIntoNestedGenericShapes() [Fact] public async Task StructuralClassifier_DistinguishesArrayFromDictionary() { - ArrayOrDictionaryUnion? array = await Serializer.DeserializeWrapper("[1,2,3]", s_options); + ArrayOrDictionaryUnion? array = await Serializer.DeserializeWrapper("[1,2,3]", _options); Assert.NotNull(array); Assert.Equal([1, 2, 3], Assert.IsType>(GetUnionValue(array))); - ArrayOrDictionaryUnion? dictionary = await Serializer.DeserializeWrapper("""{"one":1,"two":2}""", s_options); + ArrayOrDictionaryUnion? dictionary = await Serializer.DeserializeWrapper("""{"one":1,"two":2}""", _options); Assert.NotNull(dictionary); Assert.Equal(2, Assert.IsType>(GetUnionValue(dictionary))["two"]); } @@ -113,7 +108,7 @@ public async Task StructuralClassifier_DistinguishesArrayFromDictionary() [Fact] public async Task StructuralClassifier_PrefersObjectShapeOverDictionaryWhenPropertiesMatch() { - ObjectOrDictionaryUnion? result = await Serializer.DeserializeWrapper("""{"X":1,"Y":2}""", s_options); + ObjectOrDictionaryUnion? result = await Serializer.DeserializeWrapper("""{"X":1,"Y":2}""", _options); Assert.NotNull(result); Point point = Assert.IsType(GetUnionValue(result)); Assert.Equal(1, point.X); @@ -123,7 +118,7 @@ public async Task StructuralClassifier_PrefersObjectShapeOverDictionaryWhenPrope [Fact] public async Task StructuralClassifier_UsesJsonTypeInfoPropertyNames() { - RenamedPropertyUnion? result = await Serializer.DeserializeWrapper("""{"kind":"special"}""", s_options); + RenamedPropertyUnion? result = await Serializer.DeserializeWrapper("""{"kind":"special"}""", _options); Assert.NotNull(result); RenamedPropertyCase value = Assert.IsType(GetUnionValue(result)); Assert.Equal("special", value.Kind); @@ -132,7 +127,7 @@ public async Task StructuralClassifier_UsesJsonTypeInfoPropertyNames() [Fact] public async Task StructuralClassifier_UsesCaseInsensitivePropertyNames() { - PetUnion? dog = await Serializer.DeserializeWrapper("""{"name":"Rex","breed":"Labrador"}""", s_caseInsensitiveOptions); + PetUnion? dog = await Serializer.DeserializeWrapper("""{"name":"Rex","breed":"Labrador"}""", _caseInsensitiveOptions); Assert.NotNull(dog); Dog dogValue = Assert.IsType(GetUnionValue(dog)); Assert.Equal("Rex", dogValue.Name); @@ -142,7 +137,7 @@ public async Task StructuralClassifier_UsesCaseInsensitivePropertyNames() [Fact] public async Task StructuralClassifier_UsesNumberHandlingMetadata() { - NumberHandlingUnion? result = await Serializer.DeserializeWrapper("\"42\"", s_numberFromStringOptions); + NumberHandlingUnion? result = await Serializer.DeserializeWrapper("\"42\"", _numberFromStringOptions); Assert.NotNull(result); Assert.Equal(42, Assert.IsType(GetUnionValue(result))); } @@ -155,17 +150,17 @@ public async Task StructuralClassifier_UsesNumberHandlingMetadata() [InlineData("true", typeof(PetUnion))] public async Task StructuralClassifier_AmbiguousOrUnsupportedShapesThrow(string json, Type unionType) { - await Assert.ThrowsAsync(() => Serializer.DeserializeWrapper(json, unionType, s_options)); + await Assert.ThrowsAsync(() => Serializer.DeserializeWrapper(json, unionType, _options)); } [Fact] public async Task StructuralClassifier_WorksForPolymorphicDerivedTypeMetadata() { - Shape? circle = await Serializer.DeserializeWrapper("""{"Color":"red","Radius":2.5}""", s_options); + Shape? circle = await Serializer.DeserializeWrapper("""{"Color":"red","Radius":2.5}""", _options); Assert.NotNull(circle); Assert.Equal(2.5, Assert.IsType(circle).Radius); - Shape? rectangle = await Serializer.DeserializeWrapper("""{"Color":"blue","Width":3,"Height":4}""", s_options); + Shape? rectangle = await Serializer.DeserializeWrapper("""{"Color":"blue","Width":3,"Height":4}""", _options); Assert.NotNull(rectangle); Assert.Equal(4, Assert.IsType(rectangle).Height); } diff --git a/src/libraries/System.Text.Json/tests/Common/UnionTests.cs b/src/libraries/System.Text.Json/tests/Common/UnionTests.cs index d5996eef7e1899..ad73d52ce1b582 100644 --- a/src/libraries/System.Text.Json/tests/Common/UnionTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/UnionTests.cs @@ -182,7 +182,7 @@ public void PerTypeTypeClassifier_IsVisibleToModifier() // Trigger configuration of the type info via GetTypeInfo (modifiers run during // resolver invocation, before Configure). - options.MakeReadOnly(populateMissingResolver: true); + options.MakeReadOnly(); _ = options.GetTypeInfo(typeof(UnionWithCustomClassifier)); Assert.NotNull(observedAtModifierTime); @@ -323,7 +323,7 @@ public void OptionsTypeClassifier_IsVisibleToModifier() }); options.TypeClassifiers.Add(new UnionWithOtherCaseOptionsClassifierFactory()); - options.MakeReadOnly(populateMissingResolver: true); + options.MakeReadOnly(); _ = options.GetTypeInfo(typeof(UnionWithCustomConverterCase)); Assert.NotNull(observedAtModifierTime); @@ -1140,7 +1140,7 @@ public void Configure_ThrowsWhenModifierClearsUnionConstructor() } }); - options.MakeReadOnly(populateMissingResolver: true); + options.MakeReadOnly(); InvalidOperationException ex = Assert.Throws( () => options.GetTypeInfo(typeof(NullableCaseUnion))); @@ -1160,7 +1160,7 @@ public void Configure_ThrowsWhenModifierClearsUnionDeconstructor() } }); - options.MakeReadOnly(populateMissingResolver: true); + options.MakeReadOnly(); InvalidOperationException ex = Assert.Throws( () => options.GetTypeInfo(typeof(NullableCaseUnion))); @@ -1779,7 +1779,7 @@ public void CyclicUnion_Serialize_ThrowsJsonExceptionAtMaxDepth() }; Assert.Throws(() => - JsonSerializer.Serialize(union, options)); + JsonSerializer.Serialize(union, options.GetTypeInfo())); } [Fact] diff --git a/src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs b/src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs index 37473029fc0f8e..ce8978778c2af6 100644 --- a/src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs +++ b/src/libraries/System.Text.Json/tests/Common/UnsupportedTypesTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.Serialization; using System.Threading; @@ -168,9 +169,10 @@ public override void Write(Utf8JsonWriter writer, IntPtr value, JsonSerializerOp } } -#if !BUILDING_SOURCE_GENERATOR_TESTS [Fact] [SkipOnTargetFramework(TargetFrameworkMonikers.Netcoreapp, "NullableContextAttribute is public in corelib in .NET 8+")] + [RequiresUnreferencedCode("Uses reflection to construct and serialize an arbitrary runtime type.")] + [RequiresDynamicCode("Uses reflection to construct and serialize an arbitrary runtime type.")] public async Task TypeWithNullConstructorParameterName_ThrowsNotSupportedException() { // Regression test for https://github.com/dotnet/runtime/issues/58690 @@ -182,7 +184,6 @@ public async Task TypeWithNullConstructorParameterName_ThrowsNotSupportedExcepti await Assert.ThrowsAnyAsync(() => Serializer.SerializeWrapper(value)); await Assert.ThrowsAnyAsync(() => Serializer.DeserializeWrapper("{}", type)); } -#endif [Fact] public async Task RuntimeConverterIsSupported_IntPtr() diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs index 88f59426a2105e..133756c4d9eb27 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSerializerContextTests.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; @@ -121,6 +122,8 @@ static void AssertGenericContext(JsonSerializerContext context) } [Fact] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static async Task SupportsBoxedRootLevelValues() { PersonJsonContext context = PersonJsonContext.Default; @@ -293,6 +296,8 @@ public static void CombiningContexts_ResolveJsonTypeInfo_DifferentCasing() } [Fact] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static void FastPathSerialization_ResolvingJsonTypeInfo() { JsonSerializerOptions options = FastPathSerializationContext.Default.Options; @@ -314,6 +319,8 @@ public static void FastPathSerialization_ResolvingJsonTypeInfo() [Theory] [MemberData(nameof(GetFastPathCompatibleResolvers))] [MemberData(nameof(GetFastPathIncompatibleResolvers))] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static void FastPathSerialization_AppendedResolver_WorksAsExpected(IJsonTypeInfoResolver appendedResolver) { // Resolvers appended after ours will never introduce metadata to the type graph, @@ -356,6 +363,8 @@ public static void FastPathSerialization_AppendedResolver_WorksAsExpected(IJsonT [Theory] [MemberData(nameof(GetFastPathCompatibleResolvers))] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static void FastPathSerialization_PrependedResolver_CompatibleResolvers_WorksAsExpected(IJsonTypeInfoResolver prependedResolver) { // We're prepending a resolver that generates metadata for the property of our type, @@ -398,6 +407,8 @@ public static void FastPathSerialization_PrependedResolver_CompatibleResolvers_W [Theory] [MemberData(nameof(GetFastPathIncompatibleResolvers))] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static void FastPathSerialization_PrependedResolver_IncompatibleResolvers_FallsBackToMetadata(IJsonTypeInfoResolver prependedResolver) { // We're prepending a resolver that generates metadata for the property of our type, @@ -438,6 +449,8 @@ public static void FastPathSerialization_PrependedResolver_IncompatibleResolvers Assert.Equal(0, fastPathContext.FastPathInvocationCount); } + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static IEnumerable GetFastPathCompatibleResolvers() { yield return new object[] { CompatibleWithInstrumentedFastPathContext.Default }; @@ -446,6 +459,8 @@ public static IEnumerable GetFastPathCompatibleResolvers() yield return new object[] { new CustomWrappingResolver { Resolver = new ContextWithInstrumentedFastPath() } }; } + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static IEnumerable GetFastPathIncompatibleResolvers() { yield return new object[] { NotCompatibleWithInstrumentedFastPathContext.Default }; @@ -541,6 +556,8 @@ public partial class FastPathSerializationContext : JsonSerializerContext [Theory] [MemberData(nameof(GetCombiningContextsData))] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static void CombiningContexts_Serialization(T value, string expectedJson) { IJsonTypeInfoResolver combined = JsonTypeInfoResolver.Combine(NestedContext.Default, PersonJsonContext.Default); @@ -560,6 +577,8 @@ public static void CombiningContexts_Serialization(T value, string expectedJs [Theory] [MemberData(nameof(GetCombiningContextsData))] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static void ChainedContexts_Serialization(T value, string expectedJson) { var options = new JsonSerializerOptions { TypeInfoResolverChain = { NestedContext.Default, PersonJsonContext.Default } }; @@ -577,6 +596,8 @@ public static void ChainedContexts_Serialization(T value, string expectedJson } [Fact] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static void CombiningContextWithCustomResolver_ReplacePoco() { TestResolver customResolver = new((type, options) => @@ -725,7 +746,7 @@ public static void ClassWithStringValuesRoundtrips() StringValuesProperty = new(new[] { "abc", "def" }) }; - string json = JsonSerializer.Serialize(obj, options); + string json = JsonSerializer.Serialize(obj, options.GetTypeInfo()); Assert.Equal("""{"StringValuesProperty":["abc","def"]}""", json); } @@ -741,7 +762,7 @@ public static void ClassWithDictionaryPropertyRoundtrips() ["test"] = "baz", }); - string json = JsonSerializer.Serialize(obj, options); + string json = JsonSerializer.Serialize(obj, options.GetTypeInfo()); Assert.Equal("""{"DictionaryProperty":{"foo":"bar","test":"baz"}}""", json); } @@ -819,6 +840,8 @@ public TestResolver(Func getTypeInfo } [Fact] + [RequiresUnreferencedCode("Tests reflection-based JsonSerializer APIs.")] + [RequiresDynamicCode("Tests reflection-based JsonSerializer APIs.")] public static void FastPathSerialization_EvaluatePropertyOnlyOnceWhenIgnoreNullOrDefaultIsSpecified() { JsonSerializerOptions options = FastPathSerializationContext.Default.Options; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSourceGenerationOptionsTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSourceGenerationOptionsTests.cs index 083247c10ceed6..2063fe0cfec491 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSourceGenerationOptionsTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/JsonSourceGenerationOptionsTests.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json.Serialization; using Xunit; @@ -10,6 +11,7 @@ namespace System.Text.Json.SourceGeneration.Tests public static partial class JsonSourceGenerationOptionsTests { [Fact] + [RequiresUnreferencedCode("AssertOptionsEqual uses reflection to enumerate JsonSerializerOptions properties.")] public static void ContextWithGeneralSerializerDefaults_GeneratesExpectedOptions() { JsonSerializerOptions expected = new(JsonSerializerDefaults.General) { TypeInfoResolver = ContextWithGeneralSerializerDefaults.Default }; @@ -24,6 +26,7 @@ public partial class ContextWithGeneralSerializerDefaults : JsonSerializerContex { } [Fact] + [RequiresUnreferencedCode("AssertOptionsEqual uses reflection to enumerate JsonSerializerOptions properties.")] public static void ContextWithWebSerializerDefaults_GeneratesExpectedOptions() { JsonSerializerOptions expected = new(JsonSerializerDefaults.Web) { TypeInfoResolver = ContextWithWebSerializerDefaults.Default }; @@ -38,6 +41,7 @@ public partial class ContextWithWebSerializerDefaults : JsonSerializerContext { } [Fact] + [RequiresUnreferencedCode("AssertOptionsEqual uses reflection to enumerate JsonSerializerOptions properties.")] public static void ContextWithStrictSerializerDefaults_GeneratesExpectedOptions() { JsonSerializerOptions expected = new(JsonSerializerDefaults.Strict) { TypeInfoResolver = ContextWithStrictSerializerDefaults.Default }; @@ -52,6 +56,7 @@ public partial class ContextWithStrictSerializerDefaults : JsonSerializerContext { } [Fact] + [RequiresUnreferencedCode("AssertOptionsEqual uses reflection to enumerate JsonSerializerOptions properties.")] public static void ContextWithWebDefaultsAndOverriddenPropertyNamingPolicy_GeneratesExpectedOptions() { JsonSerializerOptions expected = new(JsonSerializerDefaults.Web) @@ -71,6 +76,7 @@ public partial class ContextWithWebDefaultsAndOverriddenPropertyNamingPolicy : J { } [Fact] + [RequiresUnreferencedCode("AssertOptionsEqual uses reflection to enumerate JsonSerializerOptions properties.")] public static void ContextWithAllOptionsSet_GeneratesExpectedOptions() { JsonSerializerOptions expected = new(JsonSerializerDefaults.Web) diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs index 3cfcdcdc03a7e9..bf96dc4714c45f 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs @@ -777,7 +777,7 @@ public class MyNestedNestedClass [Fact] public void ConstructingFromOptionsKeepsReference() { - JsonStringEnumConverter converter = new(); + JsonConverter converter = new JsonStringEnumConverter(); JsonSerializerOptions options = new() { PropertyNameCaseInsensitive = true, diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.SourceGen.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.SourceGen.cs index b54ad5c16db648..2822d6a5b59882 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.SourceGen.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/JsonSerializerWrapper.SourceGen.cs @@ -23,10 +23,10 @@ public StringSerializerWrapper(JsonSerializerContext defaultContext) public override JsonSerializerOptions DefaultOptions => _defaultContext.Options; public override Task SerializeWrapper(object value, Type type, JsonSerializerOptions? options = null) - => Task.FromResult(JsonSerializer.Serialize(value, type, GetOptions(options))); + => Task.FromResult(JsonSerializer.Serialize(value, GetOptions(options).GetTypeInfo(type))); public override Task SerializeWrapper(T value, JsonSerializerOptions? options = null) - => Task.FromResult(JsonSerializer.Serialize(value, GetOptions(options))); + => Task.FromResult(JsonSerializer.Serialize(value, GetOptions(options).GetTypeInfo())); public override Task SerializeWrapper(object value, Type inputType, JsonSerializerContext context) => Task.FromResult(JsonSerializer.Serialize(value, inputType, context)); @@ -38,10 +38,10 @@ public override Task SerializeWrapper(object value, JsonTypeInfo jsonTyp => Task.FromResult(JsonSerializer.Serialize(value, jsonTypeInfo)); public override Task DeserializeWrapper(string json, JsonSerializerOptions? options = null) - => Task.FromResult(JsonSerializer.Deserialize(json, GetOptions(options))); + => Task.FromResult(JsonSerializer.Deserialize(json, GetOptions(options).GetTypeInfo())); public override Task DeserializeWrapper(string json, Type type, JsonSerializerOptions? options = null) - => Task.FromResult(JsonSerializer.Deserialize(json, type, GetOptions(options))); + => Task.FromResult(JsonSerializer.Deserialize(json, GetOptions(options).GetTypeInfo(type))); public override Task DeserializeWrapper(string json, JsonTypeInfo jsonTypeInfo) => Task.FromResult(JsonSerializer.Deserialize(json, jsonTypeInfo)); @@ -71,7 +71,7 @@ private JsonSerializerOptions GetOptions(JsonSerializerOptions? options = null) public override IAsyncEnumerable DeserializeAsyncEnumerable(Stream utf8Json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default) { - return JsonSerializer.DeserializeAsyncEnumerable(utf8Json, GetOptions(options), cancellationToken); + return JsonSerializer.DeserializeAsyncEnumerable(utf8Json, GetOptions(options).GetTypeInfo(), cancellationToken); } public override IAsyncEnumerable DeserializeAsyncEnumerable(Stream utf8Json, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) @@ -86,7 +86,7 @@ public override IAsyncEnumerable DeserializeAsyncEnumerable(Stream utf8Jso public override IAsyncEnumerable DeserializeAsyncEnumerable(Stream utf8Json, bool topLevelValues, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { - return JsonSerializer.DeserializeAsyncEnumerable(utf8Json, topLevelValues, GetOptions(options), cancellationToken); + return JsonSerializer.DeserializeAsyncEnumerable(utf8Json, GetOptions(options).GetTypeInfo(), topLevelValues, cancellationToken); } } @@ -104,12 +104,12 @@ public AsyncStreamSerializerWrapper(JsonSerializerContext defaultContext) public override async Task DeserializeWrapper(Stream utf8Json, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { - return await JsonSerializer.DeserializeAsync(utf8Json, GetOptions(options), cancellationToken); + return await JsonSerializer.DeserializeAsync(utf8Json, GetOptions(options).GetTypeInfo(), cancellationToken); } public override async Task DeserializeWrapper(Stream utf8Json, Type returnType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default) { - return await JsonSerializer.DeserializeAsync(utf8Json, returnType, GetOptions(options), cancellationToken); + return await JsonSerializer.DeserializeAsync(utf8Json, GetOptions(options).GetTypeInfo(returnType), cancellationToken); } public override async Task DeserializeWrapper(Stream utf8Json, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) @@ -128,10 +128,10 @@ public override async Task DeserializeWrapper(Stream utf8Json, Type retu } public override Task SerializeWrapper(Stream stream, T value, JsonSerializerOptions options = null, CancellationToken cancellationToken = default) - => JsonSerializer.SerializeAsync(stream, value, GetOptions(options), cancellationToken); + => JsonSerializer.SerializeAsync(stream, value, GetOptions(options).GetTypeInfo(), cancellationToken); public override Task SerializeWrapper(Stream stream, object value, Type inputType, JsonSerializerOptions options = null, CancellationToken cancellationToken = default) - => JsonSerializer.SerializeAsync(stream, value, inputType, GetOptions(options), cancellationToken); + => JsonSerializer.SerializeAsync(stream, value, GetOptions(options).GetTypeInfo(inputType), cancellationToken); public override Task SerializeWrapper(Stream stream, T value, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) => JsonSerializer.SerializeAsync(stream, value, jsonTypeInfo, cancellationToken); @@ -161,7 +161,7 @@ private JsonSerializerOptions GetOptions(JsonSerializerOptions? options = null) public override IAsyncEnumerable DeserializeAsyncEnumerable(Stream utf8Json, JsonSerializerOptions options = null, CancellationToken cancellationToken = default) { - return JsonSerializer.DeserializeAsyncEnumerable(utf8Json, GetOptions(options), cancellationToken); + return JsonSerializer.DeserializeAsyncEnumerable(utf8Json, GetOptions(options).GetTypeInfo(), cancellationToken); } public override IAsyncEnumerable DeserializeAsyncEnumerable(Stream utf8Json, JsonTypeInfo jsonTypeInfo, CancellationToken cancellationToken = default) @@ -176,7 +176,7 @@ public override IAsyncEnumerable DeserializeAsyncEnumerable(Stream utf8Jso public override IAsyncEnumerable DeserializeAsyncEnumerable(Stream utf8Json, bool topLevelValues, JsonSerializerOptions? options = null, CancellationToken cancellationToken = default) { - return JsonSerializer.DeserializeAsyncEnumerable(utf8Json, topLevelValues, GetOptions(options), cancellationToken); + return JsonSerializer.DeserializeAsyncEnumerable(utf8Json, GetOptions(options).GetTypeInfo(), topLevelValues, cancellationToken); } } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs index 78c2aa445ce340..7b5915aa6ac00c 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PolymorphicTests.cs @@ -241,6 +241,16 @@ public PolymorphicTests_Metadata() [JsonSerializable(typeof(PolymorphicTests.MyClass))] [JsonSerializable(typeof(PolymorphicTests.MyThingCollection))] [JsonSerializable(typeof(PolymorphicTests.MyThingDictionary))] + [JsonSerializable(typeof(PolymorphicTests.MyDerivedThing))] + [JsonSerializable(typeof(List))] + [JsonSerializable(typeof(Queue))] + [JsonSerializable(typeof(ISet))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(SortedDictionary))] + [JsonSerializable(typeof(IReadOnlyDictionary))] + [JsonSerializable(typeof(PolymorphicClass.DerivedAbstractClass), TypeInfoPropertyName = "PolymorphicClass_DerivedAbstractClass")] + [JsonSerializable(typeof(PolymorphicInterface.DerivedInterface1))] + [JsonSerializable(typeof(PolymorphicInterface.DerivedInterface2))] internal sealed partial class PolymorphicTestsContext_Metadata : JsonSerializerContext { } @@ -486,6 +496,7 @@ public PolymorphicTests_Default() [JsonSerializable(typeof(PolymorphicTests.MyClass))] [JsonSerializable(typeof(PolymorphicTests.MyThingCollection))] [JsonSerializable(typeof(PolymorphicTests.MyThingDictionary))] + [JsonSerializable(typeof(PolymorphicTests.MyDerivedThing))] internal sealed partial class PolymorphicTestsContext_Default : JsonSerializerContext { } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs index a5e6b752e20a0f..c7bd164df930ec 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/PropertyVisibilityTests.cs @@ -1,7 +1,8 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using System.Text.Json.Serialization; @@ -27,7 +28,7 @@ protected PropertyVisibilityTests_Metadata(Serialization.Tests.JsonSerializerWra [Theory] [InlineData(typeof(ClassWithBadIgnoreAttribute))] [InlineData(typeof(StructWithBadIgnoreAttribute))] - public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson(Type type) + public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { InvalidOperationException ioe = await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper("", type)); ValidateInvalidOperationException(); @@ -65,7 +66,7 @@ public override async Task Honor_JsonSerializablePropertyAttribute_OnProperties( [InlineData(typeof(Class_PropertyWith_PrivateInitOnlySetter_WithAttribute))] [InlineData(typeof(Class_PropertyWith_InternalInitOnlySetter_WithAttribute))] [InlineData(typeof(Class_PropertyWith_ProtectedInitOnlySetter_WithAttribute))] - public override async Task NonPublicInitOnlySetter_With_JsonInclude(Type type) + public override async Task NonPublicInitOnlySetter_With_JsonInclude([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { // Inaccessible [JsonInclude] members are ignored (https://github.com/dotnet/runtime/issues/124889). // All types have public getter so serialization works; internal setter type can also deserialize. @@ -94,7 +95,7 @@ public override async Task HonorCustomConverter_UsingPrivateSetter() // MyEnum (private get, public set): setter works, getter excluded. // MyInt (public get, private set): getter works (with converter), setter excluded. var options = new JsonSerializerOptions(); - options.Converters.Add(new JsonStringEnumConverter()); + options.Converters.Add(new JsonStringEnumConverter()); string json = """{"MyEnum":"AnotherValue","MyInt":2}"""; var obj = await Serializer.DeserializeWrapper(json, options); @@ -147,7 +148,7 @@ public override async Task HonorJsonPropertyName_PrivateSetter() Assert.Equal(0, obj.MyInt); } - public override async Task NonPublicProperty_JsonInclude_WorksAsExpected(Type type, bool isAccessibleBySourceGen) + public override async Task NonPublicProperty_JsonInclude_WorksAsExpected([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] Type type, bool isAccessibleBySourceGen) { // Inaccessible [JsonInclude] members are ignored (https://github.com/dotnet/runtime/issues/124889). if (isAccessibleBySourceGen) @@ -580,7 +581,7 @@ public PropertyVisibilityTests_Default() [Theory] [InlineData(typeof(ClassWithBadIgnoreAttribute))] [InlineData(typeof(StructWithBadIgnoreAttribute))] - public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail(Type type) + public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper("{}", type)); await Assert.ThrowsAsync(async () => await Serializer.SerializeWrapper(Activator.CreateInstance(type), type)); @@ -589,7 +590,7 @@ public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail( [Theory] [InlineData(typeof(ClassWithBadIgnoreAttribute))] [InlineData(typeof(StructWithBadIgnoreAttribute))] - public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson(Type type) + public override async Task JsonIgnoreCondition_WhenWritingNull_OnValueType_Fail_EmptyJson([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type) { // Since this code goes down fast-path, there's no warm up and we hit the reader exception about having no tokens. await Assert.ThrowsAsync(async () => await Serializer.DeserializeWrapper("", type)); @@ -612,15 +613,15 @@ public void PublicContextAndTestClassWithPropertiesWithDifferentAccessibilities( PublicField = new(), }; - string json = JsonSerializer.Serialize(obj, options); + string json = JsonSerializer.Serialize(obj, options.GetTypeInfo()); Assert.Equal("""{"PublicProperty":{},"PublicField":{}}""", json); - var deserialized = JsonSerializer.Deserialize(json, options); + var deserialized = JsonSerializer.Deserialize(json, options.GetTypeInfo()); Assert.NotNull(deserialized.PublicProperty); Assert.NotNull(deserialized.PublicField); json = "{}"; - deserialized = JsonSerializer.Deserialize(json, options); + deserialized = JsonSerializer.Deserialize(json, options.GetTypeInfo()); Assert.Null(deserialized.PublicProperty); Assert.Null(deserialized.PublicField); } @@ -630,8 +631,8 @@ public void PublicContextAndJsonConverter() { JsonConverter obj = JsonMetadataServices.BooleanConverter; - Assert.Throws(() => JsonSerializer.Serialize(obj, PublicContext.Default.Options)); - Assert.Throws(() => JsonSerializer.Deserialize("{}", PublicContext.Default.Options)); + Assert.Throws(() => JsonSerializer.Serialize(obj, PublicContext.Default.Options.GetTypeInfo())); + Assert.Throws(() => JsonSerializer.Deserialize("{}", PublicContext.Default.Options.GetTypeInfo())); } [Fact] @@ -644,9 +645,9 @@ public void PublicContextAndJsonSerializerOptions() IncludeFields = true, }; - string json = JsonSerializer.Serialize(obj, PublicContext.Default.Options); + string json = JsonSerializer.Serialize(obj, PublicContext.Default.Options.GetTypeInfo()); - JsonSerializerOptions deserialized = JsonSerializer.Deserialize(json, PublicContext.Default.Options); + JsonSerializerOptions deserialized = JsonSerializer.Deserialize(json, PublicContext.Default.Options.GetTypeInfo()); Assert.Equal(obj.DefaultBufferSize, deserialized.DefaultBufferSize); Assert.Equal(obj.DefaultIgnoreCondition, deserialized.DefaultIgnoreCondition); Assert.Equal(obj.IncludeFields, deserialized.IncludeFields); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs index 2e4b94b952fcf3..4e8c4677c239ba 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationLogicTests.cs @@ -83,7 +83,7 @@ public static IEnumerable GetCompatibleOptions() // Options with features that aren't supported in the generated serialization funcs. public static IEnumerable GetOptionsUsingUnsupportedFeatures() { - yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { Converters = { new JsonStringEnumConverter() } } }; + yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { Converters = { new JsonStringEnumConverter() } } }; yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping } }; yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { NumberHandling = JsonNumberHandling.WriteAsString } }; yield return new object[] { new JsonSerializerOptions(s_compatibleOptions) { NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals } }; @@ -101,7 +101,7 @@ public static IEnumerable GetOptionsUsingUnsupportedFeatures() public static IEnumerable GetIncompatibleOptions() { yield return new object[] { new JsonSerializerOptions() }; - yield return new object[] { new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } } }; + yield return new object[] { new JsonSerializerOptions { Converters = { new JsonStringEnumConverter() } } }; yield return new object[] { new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping } }; yield return new object[] { new JsonSerializerOptions { NumberHandling = JsonNumberHandling.WriteAsString } }; yield return new object[] { new JsonSerializerOptions { ReferenceHandler = ReferenceHandler.Preserve } }; diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj index ccdf792ab9b8e4..bde7513156c634 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn3.11.Tests.csproj @@ -2,9 +2,6 @@ 3.11 - - false - false diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.4.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.4.Tests.csproj index 21aa274d363219..0fa1bccdb970ba 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.4.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Roslyn4.4.Tests.csproj @@ -2,9 +2,6 @@ 4.4 - - false - false diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets index 2233ab2704d2e9..1870e21a52c758 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets @@ -174,8 +174,13 @@ + + + + + diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.CustomConverters.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.CustomConverters.cs index a049400c652092..88ccb4ed695aae 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.CustomConverters.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.CustomConverters.cs @@ -1,7 +1,8 @@ -// Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Text.Json.Serialization; +using System.Text.Json.Serialization.Metadata; namespace System.Text.Json.SourceGeneration.Tests { @@ -336,7 +337,7 @@ public override Option Read(ref Utf8JsonReader reader, Type typeToConvert, Js return default; } - return new(JsonSerializer.Deserialize(ref reader, options)!); + return new(JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!); } public override void Write(Utf8JsonWriter writer, Option value, JsonSerializerOptions options) @@ -347,7 +348,7 @@ public override void Write(Utf8JsonWriter writer, Option value, JsonSerialize return; } - JsonSerializer.Serialize(writer, value.Value, options); + JsonSerializer.Serialize(writer, value.Value, options.GetTypeInfo()); } } @@ -380,13 +381,13 @@ public sealed class GenericWrapperConverter : JsonConverter { public override GenericWrapper Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - T value = JsonSerializer.Deserialize(ref reader, options)!; + T value = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; return new GenericWrapper(value); } public override void Write(Utf8JsonWriter writer, GenericWrapper value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value.WrappedValue, options); + JsonSerializer.Serialize(writer, value.WrappedValue, options.GetTypeInfo()); } } @@ -430,9 +431,9 @@ public override TypeWithNestedConverter Read(ref Utf8JsonReader reader, Ty reader.Read(); if (propertyName == "Value1") - result.Value1 = JsonSerializer.Deserialize(ref reader, options)!; + result.Value1 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; else if (propertyName == "Value2") - result.Value2 = JsonSerializer.Deserialize(ref reader, options)!; + result.Value2 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; } return result; @@ -442,9 +443,9 @@ public override void Write(Utf8JsonWriter writer, TypeWithNestedConverter { writer.WriteStartObject(); writer.WritePropertyName("Value1"); - JsonSerializer.Serialize(writer, value.Value1, options); + JsonSerializer.Serialize(writer, value.Value1, options.GetTypeInfo()); writer.WritePropertyName("Value2"); - JsonSerializer.Serialize(writer, value.Value2, options); + JsonSerializer.Serialize(writer, value.Value2, options.GetTypeInfo()); writer.WriteEndObject(); } } @@ -477,7 +478,7 @@ public override TypeWithSatisfiedConstraint Read(ref Utf8JsonReader reader, T reader.Read(); if (propertyName == "Value") - result.Value = JsonSerializer.Deserialize(ref reader, options)!; + result.Value = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; } return result; @@ -487,7 +488,7 @@ public override void Write(Utf8JsonWriter writer, TypeWithSatisfiedConstraint { writer.WriteStartObject(); writer.WritePropertyName("Value"); - JsonSerializer.Serialize(writer, value.Value, options); + JsonSerializer.Serialize(writer, value.Value, options.GetTypeInfo()); writer.WriteEndObject(); } } @@ -524,9 +525,9 @@ public override TypeWithDeeplyNestedConverter Read(ref Utf8JsonReader read reader.Read(); if (propertyName == "Value1") - result.Value1 = JsonSerializer.Deserialize(ref reader, options)!; + result.Value1 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; else if (propertyName == "Value2") - result.Value2 = JsonSerializer.Deserialize(ref reader, options)!; + result.Value2 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; } return result; @@ -536,9 +537,9 @@ public override void Write(Utf8JsonWriter writer, TypeWithDeeplyNestedConverter< { writer.WriteStartObject(); writer.WritePropertyName("Value1"); - JsonSerializer.Serialize(writer, value.Value1, options); + JsonSerializer.Serialize(writer, value.Value1, options.GetTypeInfo()); writer.WritePropertyName("Value2"); - JsonSerializer.Serialize(writer, value.Value2, options); + JsonSerializer.Serialize(writer, value.Value2, options.GetTypeInfo()); writer.WriteEndObject(); } } @@ -581,11 +582,11 @@ public override TypeWithManyParams Read(ref Utf8JsonReader reader switch (propertyName) { - case "Value1": result.Value1 = JsonSerializer.Deserialize(ref reader, options)!; break; - case "Value2": result.Value2 = JsonSerializer.Deserialize(ref reader, options)!; break; - case "Value3": result.Value3 = JsonSerializer.Deserialize(ref reader, options)!; break; - case "Value4": result.Value4 = JsonSerializer.Deserialize(ref reader, options)!; break; - case "Value5": result.Value5 = JsonSerializer.Deserialize(ref reader, options)!; break; + case "Value1": result.Value1 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; break; + case "Value2": result.Value2 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; break; + case "Value3": result.Value3 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; break; + case "Value4": result.Value4 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; break; + case "Value5": result.Value5 = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; break; } } @@ -596,15 +597,15 @@ public override void Write(Utf8JsonWriter writer, TypeWithManyParams()); writer.WritePropertyName("Value2"); - JsonSerializer.Serialize(writer, value.Value2, options); + JsonSerializer.Serialize(writer, value.Value2, options.GetTypeInfo()); writer.WritePropertyName("Value3"); - JsonSerializer.Serialize(writer, value.Value3, options); + JsonSerializer.Serialize(writer, value.Value3, options.GetTypeInfo()); writer.WritePropertyName("Value4"); - JsonSerializer.Serialize(writer, value.Value4, options); + JsonSerializer.Serialize(writer, value.Value4, options.GetTypeInfo()); writer.WritePropertyName("Value5"); - JsonSerializer.Serialize(writer, value.Value5, options); + JsonSerializer.Serialize(writer, value.Value5, options.GetTypeInfo()); writer.WriteEndObject(); } } @@ -640,7 +641,7 @@ public override TypeWithSingleLevelNestedConverter Read(ref Utf8JsonReader re reader.Read(); if (propertyName == "Value") - result.Value = JsonSerializer.Deserialize(ref reader, options)!; + result.Value = JsonSerializer.Deserialize(ref reader, options.GetTypeInfo())!; } return result; @@ -650,7 +651,7 @@ public override void Write(Utf8JsonWriter writer, TypeWithSingleLevelNestedConve { writer.WriteStartObject(); writer.WritePropertyName("Value"); - JsonSerializer.Serialize(writer, value.Value, options); + JsonSerializer.Serialize(writer, value.Value, options.GetTypeInfo()); writer.WriteEndObject(); } } diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs index e0da363e9d8d74..4f30b99bca8edb 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonValueTests.cs @@ -994,7 +994,7 @@ public static void JsonValue_CreateFromDouble() [Fact] public static void JsonValue_CreateWithJsonTypeInfo() { - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(int)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); JsonValue value = JsonValue.Create(42, typeInfo); Assert.Equal(42, value.GetValue()); @@ -1003,7 +1003,7 @@ public static void JsonValue_CreateWithJsonTypeInfo() [Fact] public static void JsonValue_CreateWithJsonTypeInfoAndOptions() { - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(string)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); var nodeOptions = new JsonNodeOptions { PropertyNameCaseInsensitive = true }; JsonValue value = JsonValue.Create("test", typeInfo, nodeOptions); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs index bf11f1cbf8b875..a00d4784f556c4 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/DomTests.cs @@ -253,7 +253,7 @@ public static void SerializeToNode_WithEscaping() [Fact] public static void SerializeToDocument_WithJsonTypeInfo() { - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = MyPoco.Create(); using JsonDocument dom = JsonSerializer.SerializeToDocument(obj, typeInfo); @@ -279,7 +279,7 @@ public static void SerializeToDocument_WithJsonTypeInfo_NonGeneric() [Fact] public static void SerializeToElement_WithJsonTypeInfo() { - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = MyPoco.Create(); JsonElement element = JsonSerializer.SerializeToElement(obj, typeInfo); @@ -305,7 +305,7 @@ public static void SerializeToElement_WithJsonTypeInfo_NonGeneric() [Fact] public static void SerializeToNode_WithJsonTypeInfo() { - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = MyPoco.Create(); JsonNode node = JsonSerializer.SerializeToNode(obj, typeInfo); @@ -331,7 +331,7 @@ public static void DeserializeFromSpan_WithJsonTypeInfo() { ReadOnlySpan utf8Json = """{"StringProp":"Hello","IntArrayProp":[1,2]}"""u8; - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = JsonSerializer.Deserialize(utf8Json, typeInfo); obj.Verify(); @@ -381,7 +381,7 @@ public static void DeserializeFromSpan_NullValue() { ReadOnlySpan utf8Json = "null"u8; - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = JsonSerializer.Deserialize(utf8Json, typeInfo); Assert.Null(obj); @@ -392,7 +392,7 @@ public static void DeserializeFromJsonDocument_WithJsonTypeInfo() { using JsonDocument doc = JsonDocument.Parse(Json); - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = doc.Deserialize(typeInfo); obj.Verify(); @@ -416,7 +416,7 @@ public static void DeserializeFromJsonElement_WithJsonTypeInfo() using JsonDocument doc = JsonDocument.Parse(Json); JsonElement element = doc.RootElement; - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = element.Deserialize(typeInfo); obj.Verify(); @@ -440,7 +440,7 @@ public static void DeserializeFromJsonNode_WithJsonTypeInfo() { JsonNode node = JsonNode.Parse(Json); - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = node.Deserialize(typeInfo); obj.Verify(); @@ -463,7 +463,7 @@ public static void DeserializeFromCharSpan_WithJsonTypeInfo() { ReadOnlySpan jsonChars = Json.AsSpan(); - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = JsonSerializer.Deserialize(jsonChars, typeInfo); obj.Verify(); @@ -531,7 +531,7 @@ public static void DeserializeFromUtf8JsonReader_WithJsonTypeInfo() byte[] utf8Json = Encoding.UTF8.GetBytes(Json); Utf8JsonReader reader = new(utf8Json); - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = JsonSerializer.Deserialize(ref reader, typeInfo); obj.Verify(); @@ -555,7 +555,7 @@ public static void DeserializeFromStream_WithJsonTypeInfo() { using MemoryStream stream = new(Encoding.UTF8.GetBytes(Json)); - JsonTypeInfo typeInfo = (JsonTypeInfo)JsonSerializerOptions.Default.GetTypeInfo(typeof(MyPoco)); + JsonTypeInfo typeInfo = JsonSerializerOptions.Default.GetTypeInfo(); MyPoco obj = JsonSerializer.Deserialize(stream, typeInfo); obj.Verify(); diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj index 7fed121a4f9c9b..e152d9e04a93be 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj @@ -277,9 +277,14 @@ + + + + +