Subject src-gen serializer to full number-handling test (#87484)
authorEirik Tsarpalis <eirik.tsarpalis@gmail.com>
Tue, 13 Jun 2023 21:10:48 +0000 (22:10 +0100)
committerGitHub <noreply@github.com>
Tue, 13 Jun 2023 21:10:48 +0000 (22:10 +0100)
* Subject src-gen serializer to full number-handling tests

* Make failing tests pass

* Revert API break and use contract APIs instead

* React to test infra changes

* Fix rebase conflicts & ensure all tests are passing.

---------

Co-authored-by: Layomi Akinrinade <laakinri@microsoft.com>
21 files changed:
src/libraries/System.Text.Json/Common/JsonHelpers.cs
src/libraries/System.Text.Json/gen/JsonSourceGenerator.Emitter.cs
src/libraries/System.Text.Json/gen/Model/TypeGenerationSpec.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/JsonMetadataServicesConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonSerializerContext.cs
src/libraries/System.Text.Json/tests/Common/JsonNumberTestData.cs [moved from src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNumberTestData.cs with 100% similarity]
src/libraries/System.Text.Json/tests/Common/JsonTestHelper.cs
src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/Common/TestClasses/TestClasses.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/ContextClasses.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataAndSerializationContextTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MetadataContextTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/MixedModeContextTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/RealWorldContextTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/NumberHandlingTests.cs [new file with mode: 0644]
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/SerializationContextTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/System.Text.Json.SourceGeneration.Tests.targets
src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/TestClasses.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonTestHelper.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/NumberHandlingTests.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/System.Text.Json.Tests.csproj

index 523d2fd..5711afa 100644 (file)
@@ -5,6 +5,7 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Runtime.InteropServices;
+using System.Text.Json.Serialization;
 
 namespace System.Text.Json
 {
@@ -41,6 +42,13 @@ namespace System.Text.Json
         }
 #endif
 
+        internal static bool RequiresSpecialNumberHandlingOnWrite(JsonNumberHandling? handling)
+        {
+            return handling != null
+                ? (handling.Value & (JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowNamedFloatingPointLiterals)) != 0
+                : false;
+        }
+
         /// <summary>
         /// Provides an in-place, stable sorting implementation for List.
         /// </summary>
index f8fc92c..068ee52 100644 (file)
@@ -29,7 +29,7 @@ namespace System.Text.Json.SourceGeneration
             private const string PreferredPropertyObjectCreationHandlingPropName = "PreferredPropertyObjectCreationHandling";
             private const string ObjectCreatorPropName = "ObjectCreator";
             private const string OptionsInstanceVariableName = "Options";
-            private const string JsonTypeInfoReturnValueLocalVariableName = "jsonTypeInfo";
+            private const string JsonTypeInfoLocalVariableName = "jsonTypeInfo";
             private const string PropInitMethodNameSuffix = "PropInit";
             private const string TryGetTypeInfoForRuntimeCustomConverterMethodName = "TryGetTypeInfoForRuntimeCustomConverter";
             private const string ExpandConverterMethodName = "ExpandConverter";
@@ -228,7 +228,7 @@ namespace System.Text.Json.SourceGeneration
 
                 GenerateTypeInfoFactoryHeader(writer, typeMetadata);
                 writer.WriteLine($"""
-                    {JsonTypeInfoReturnValueLocalVariableName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeFQN)}({OptionsLocalVariableName}, {JsonMetadataServicesTypeRef}.{typeInfoPropertyName}Converter);
+                    {JsonTypeInfoLocalVariableName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeFQN)}({OptionsLocalVariableName}, {JsonMetadataServicesTypeRef}.{typeInfoPropertyName}Converter);
                     """);
 
                 GenerateTypeInfoFactoryFooter(writer);
@@ -249,7 +249,7 @@ namespace System.Text.Json.SourceGeneration
 
                 writer.WriteLine($"""
                     {JsonConverterTypeRef} converter = {ExpandConverterMethodName}(typeof({typeFQN}), new {converterFQN}(), {OptionsLocalVariableName});
-                    {JsonTypeInfoReturnValueLocalVariableName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeFQN)} ({OptionsLocalVariableName}, converter);
+                    {JsonTypeInfoLocalVariableName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeFQN)} ({OptionsLocalVariableName}, converter);
                     """);
 
                 GenerateTypeInfoFactoryFooter(writer);
@@ -270,7 +270,7 @@ namespace System.Text.Json.SourceGeneration
 
                 writer.WriteLine($$"""
                     {{JsonConverterTypeRef}} converter = {{JsonMetadataServicesTypeRef}}.GetNullableConverter<{{underlyingTypeFQN}}>({{OptionsLocalVariableName}});
-                    {{JsonTypeInfoReturnValueLocalVariableName}} = {{JsonMetadataServicesTypeRef}}.{{GetCreateValueInfoMethodRef(typeFQN)}}({{OptionsLocalVariableName}}, converter);
+                    {{JsonTypeInfoLocalVariableName}} = {{JsonMetadataServicesTypeRef}}.{{GetCreateValueInfoMethodRef(typeFQN)}}({{OptionsLocalVariableName}}, converter);
                     """);
 
                 GenerateTypeInfoFactoryFooter(writer);
@@ -286,7 +286,7 @@ namespace System.Text.Json.SourceGeneration
 
                 GenerateTypeInfoFactoryHeader(writer, typeMetadata);
                 writer.WriteLine($"""
-                    {JsonTypeInfoReturnValueLocalVariableName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeFQN)}({OptionsLocalVariableName}, {JsonMetadataServicesTypeRef}.GetUnsupportedTypeConverter<{typeFQN}>());
+                    {JsonTypeInfoLocalVariableName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeFQN)}({OptionsLocalVariableName}, {JsonMetadataServicesTypeRef}.GetUnsupportedTypeConverter<{typeFQN}>());
                     """);
 
                 GenerateTypeInfoFactoryFooter(writer);
@@ -302,7 +302,7 @@ namespace System.Text.Json.SourceGeneration
 
                 GenerateTypeInfoFactoryHeader(writer, typeMetadata);
                 writer.WriteLine($"""
-                    {JsonTypeInfoReturnValueLocalVariableName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeFQN)}({OptionsLocalVariableName}, {JsonMetadataServicesTypeRef}.GetEnumConverter<{typeFQN}>({OptionsLocalVariableName}));
+                    {JsonTypeInfoLocalVariableName} = {JsonMetadataServicesTypeRef}.{GetCreateValueInfoMethodRef(typeFQN)}({OptionsLocalVariableName}, {JsonMetadataServicesTypeRef}.GetEnumConverter<{typeFQN}>({OptionsLocalVariableName}));
                     """);
 
                 GenerateTypeInfoFactoryFooter(writer);
@@ -374,11 +374,11 @@ namespace System.Text.Json.SourceGeneration
                     var {{InfoVarName}} = new {{JsonCollectionInfoValuesTypeRef}}<{{typeFQN}}>
                     {
                         {{ObjectCreatorPropName}} = {{FormatDefaultConstructorExpr(typeGenerationSpec)}},
-                        {{NumberHandlingPropName}} = {{GetNumberHandlingAsStr(typeGenerationSpec.NumberHandling)}},
                         {{SerializeHandlerPropName}} = {{serializeMethodName ?? "null"}}
                     };
 
-                    {{JsonTypeInfoReturnValueLocalVariableName}} = {{JsonMetadataServicesTypeRef}}.{{createCollectionMethodExpr}};
+                    {{JsonTypeInfoLocalVariableName}} = {{JsonMetadataServicesTypeRef}}.{{createCollectionMethodExpr}};
+                    {{JsonTypeInfoLocalVariableName}}.{{NumberHandlingPropName}} = {{GetNumberHandlingAsStr(typeGenerationSpec.NumberHandling)}};
                     """);
 
                 GenerateTypeInfoFactoryFooter(writer);
@@ -521,11 +521,11 @@ namespace System.Text.Json.SourceGeneration
                         ObjectWithParameterizedConstructorCreator = {{parameterizedCreatorInvocation}},
                         PropertyMetadataInitializer = {{propInitAdapterFunc ?? "null"}},
                         ConstructorParameterMetadataInitializer = {{ctorParamMetadataInitMethodName ?? "null"}},
-                        {{NumberHandlingPropName}} = {{GetNumberHandlingAsStr(typeMetadata.NumberHandling)}},
                         {{SerializeHandlerPropName}} = {{serializeMethodName ?? "null"}}
                     };
 
-                    {{JsonTypeInfoReturnValueLocalVariableName}} = {{JsonMetadataServicesTypeRef}}.CreateObjectInfo<{{typeMetadata.TypeRef.FullyQualifiedName}}>({{OptionsLocalVariableName}}, {{ObjectInfoVarName}});
+                    {{JsonTypeInfoLocalVariableName}} = {{JsonMetadataServicesTypeRef}}.CreateObjectInfo<{{typeMetadata.TypeRef.FullyQualifiedName}}>({{OptionsLocalVariableName}}, {{ObjectInfoVarName}});
+                    {{JsonTypeInfoLocalVariableName}}.{{NumberHandlingPropName}} = {{GetNumberHandlingAsStr(typeMetadata.NumberHandling)}};
                     """);
 
                 if (typeMetadata is { UnmappedMemberHandling: not null } or { PreferredPropertyObjectCreationHandling: not null })
@@ -534,12 +534,12 @@ namespace System.Text.Json.SourceGeneration
 
                     if (typeMetadata.UnmappedMemberHandling != null)
                     {
-                        writer.WriteLine($"{JsonTypeInfoReturnValueLocalVariableName}.{UnmappedMemberHandlingPropName} = {GetUnmappedMemberHandlingAsStr(typeMetadata.UnmappedMemberHandling.Value)};");
+                        writer.WriteLine($"{JsonTypeInfoLocalVariableName}.{UnmappedMemberHandlingPropName} = {GetUnmappedMemberHandlingAsStr(typeMetadata.UnmappedMemberHandling.Value)};");
                     }
 
                     if (typeMetadata.PreferredPropertyObjectCreationHandling != null)
                     {
-                        writer.WriteLine($"{JsonTypeInfoReturnValueLocalVariableName}.{PreferredPropertyObjectCreationHandlingPropName} = {GetObjectCreationHandlingAsStr(typeMetadata.PreferredPropertyObjectCreationHandling.Value)};");
+                        writer.WriteLine($"{JsonTypeInfoLocalVariableName}.{PreferredPropertyObjectCreationHandlingPropName} = {GetObjectCreationHandlingAsStr(typeMetadata.PreferredPropertyObjectCreationHandling.Value)};");
                     }
                 }
 
@@ -987,7 +987,7 @@ namespace System.Text.Json.SourceGeneration
 
                     private {{typeInfoFQN}} {{CreateTypeInfoMethodName(typeMetadata)}}({{JsonSerializerOptionsTypeRef}} {{OptionsLocalVariableName}})
                     {
-                        if (!{{TryGetTypeInfoForRuntimeCustomConverterMethodName}}<{{typeFQN}}>({{OptionsLocalVariableName}}, out {{typeInfoFQN}} {{JsonTypeInfoReturnValueLocalVariableName}}))
+                        if (!{{TryGetTypeInfoForRuntimeCustomConverterMethodName}}<{{typeFQN}}>({{OptionsLocalVariableName}}, out {{typeInfoFQN}} {{JsonTypeInfoLocalVariableName}}))
                         {
                     """);
 
@@ -1002,8 +1002,8 @@ namespace System.Text.Json.SourceGeneration
                 writer.WriteLine($$"""
                         }
 
-                        {{JsonTypeInfoReturnValueLocalVariableName}}.{{OriginatingResolverPropertyName}} = this;
-                        return {{JsonTypeInfoReturnValueLocalVariableName}};
+                        {{JsonTypeInfoLocalVariableName}}.{{OriginatingResolverPropertyName}} = this;
+                        return {{JsonTypeInfoLocalVariableName}};
                     }
                     """);
             }
@@ -1214,7 +1214,7 @@ namespace System.Text.Json.SourceGeneration
             private static string GetNumberHandlingAsStr(JsonNumberHandling? numberHandling) =>
                 numberHandling switch
                 {
-                    null => "default",
+                    null => "null",
                     >= 0 => $"({JsonNumberHandlingTypeRef}){(int)numberHandling.Value}",
                     < 0 => $"({JsonNumberHandlingTypeRef})({(int)numberHandling.Value})"
                 };
index 477113d..12dec1a 100644 (file)
@@ -93,6 +93,11 @@ namespace System.Text.Json.SourceGeneration
                 return false;
             }
 
+            if (JsonHelpers.RequiresSpecialNumberHandlingOnWrite(NumberHandling))
+            {
+                return false;
+            }
+
             switch (ClassType)
             {
                 case ClassType.Object:
@@ -104,8 +109,7 @@ namespace System.Text.Json.SourceGeneration
                     foreach (PropertyGenerationSpec property in PropertyGenSpecs)
                     {
                         if (property.PropertyType.SpecialType is SpecialType.System_Object ||
-                            property.NumberHandling is JsonNumberHandling.AllowNamedFloatingPointLiterals
-                                                    or JsonNumberHandling.WriteAsString ||
+                            property.NumberHandling != null ||
                             property.ConverterType != null)
                         {
                             return false;
index 1420d5b..debb3f0 100644 (file)
@@ -43,6 +43,7 @@ namespace System.Text.Json.Serialization.Converters
 
             if (!state.SupportContinuation &&
                 jsonTypeInfo.CanUseSerializeHandler &&
+                !JsonHelpers.RequiresSpecialNumberHandlingOnWrite(state.Current.NumberHandling) &&
                 !state.CurrentContainsMetadata) // Do not use the fast path if state needs to write metadata.
             {
                 ((JsonTypeInfo<T>)jsonTypeInfo).SerializeHandler!(writer, value);
index 07532b8..bb77e52 100644 (file)
@@ -68,7 +68,7 @@ namespace System.Text.Json.Serialization
                 options.Encoder == null &&
                 // Disallow custom number handling we'd need to honor when writing.
                 // AllowReadingFromString and Strict are fine since there's no action to take when writing.
-                (options.NumberHandling & (JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowNamedFloatingPointLiterals)) == 0 &&
+                !JsonHelpers.RequiresSpecialNumberHandlingOnWrite(options.NumberHandling) &&
                 options.ReferenceHandlingStrategy == ReferenceHandlingStrategy.None &&
 #pragma warning disable SYSLIB0020
                 !options.IgnoreNullValues && // This property is obsolete.
index 03961e9..2428e97 100644 (file)
@@ -12,6 +12,34 @@ namespace System.Text.Json
 {
     internal static partial class JsonTestHelper
     {
+#if NETCOREAPP
+        public const string DoubleFormatString = null;
+        public const string SingleFormatString = null;
+#else
+        public const string DoubleFormatString = "G17";
+        public const string SingleFormatString = "G9";
+#endif
+
+        public static float NextFloat(Random random)
+        {
+            double mantissa = (random.NextDouble() * 2.0) - 1.0;
+            double exponent = Math.Pow(2.0, random.Next(-126, 128));
+            float value = (float)(mantissa * exponent);
+            return value;
+        }
+
+        public static double NextDouble(Random random, double minValue, double maxValue)
+        {
+            double value = random.NextDouble() * (maxValue - minValue) + minValue;
+            return value;
+        }
+
+        public static decimal NextDecimal(Random random, double minValue, double maxValue)
+        {
+            double value = random.NextDouble() * (maxValue - minValue) + minValue;
+            return (decimal)value;
+        }
+
         public static void AssertJsonEqual(string expected, string actual)
         {
             using JsonDocument expectedDom = JsonDocument.Parse(expected);
diff --git a/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs b/src/libraries/System.Text.Json/tests/Common/NumberHandlingTests.cs
new file mode 100644 (file)
index 0000000..5220e17
--- /dev/null
@@ -0,0 +1,1834 @@
+// 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.Collections.Immutable;
+using System.Collections.ObjectModel;
+using System.Globalization;
+using System.Linq;
+using System.Text.Encodings.Web;
+using System.Text.Json.Tests;
+using System.Threading.Tasks;
+using Xunit;
+
+namespace System.Text.Json.Serialization.Tests
+{
+    public abstract class NumberHandlingTests : SerializerTests
+    {
+        public NumberHandlingTests(JsonSerializerWrapper serializerWrapper) : base(serializerWrapper) { }
+
+        private static readonly JsonSerializerOptions s_optionReadFromStr = new JsonSerializerOptions
+        {
+            NumberHandling = JsonNumberHandling.AllowReadingFromString
+        };
+
+        private static readonly JsonSerializerOptions s_optionWriteAsStr = new JsonSerializerOptions
+        {
+            NumberHandling = JsonNumberHandling.WriteAsString
+        };
+
+        private static readonly JsonSerializerOptions s_optionReadAndWriteFromStr = new JsonSerializerOptions
+        {
+            NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString
+        };
+
+        private static readonly JsonSerializerOptions s_optionsAllowFloatConstants = new JsonSerializerOptions
+        {
+            NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
+        };
+
+        private static readonly JsonSerializerOptions s_optionReadFromStrAllowFloatConstants = new JsonSerializerOptions
+        {
+            NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.AllowNamedFloatingPointLiterals
+        };
+
+        private static readonly JsonSerializerOptions s_optionWriteAsStrAllowFloatConstants = new JsonSerializerOptions
+        {
+            NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowNamedFloatingPointLiterals
+        };
+
+        [Fact]
+        public async Task Number_AsRootType_RoundTrip()
+        {
+            await RunAsRootTypeTest(JsonNumberTestData.Bytes);
+            await RunAsRootTypeTest(JsonNumberTestData.SBytes);
+            await RunAsRootTypeTest(JsonNumberTestData.Shorts);
+            await RunAsRootTypeTest(JsonNumberTestData.Ints);
+            await RunAsRootTypeTest(JsonNumberTestData.Longs);
+            await RunAsRootTypeTest(JsonNumberTestData.UShorts);
+            await RunAsRootTypeTest(JsonNumberTestData.UInts);
+            await RunAsRootTypeTest(JsonNumberTestData.ULongs);
+            await RunAsRootTypeTest(JsonNumberTestData.Floats);
+            await RunAsRootTypeTest(JsonNumberTestData.Doubles);
+            await RunAsRootTypeTest(JsonNumberTestData.Decimals);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableBytes);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableSBytes);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableShorts);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableInts);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableLongs);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableUShorts);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableUInts);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableULongs);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableFloats);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableDoubles);
+            await RunAsRootTypeTest(JsonNumberTestData.NullableDecimals);
+        }
+
+        private async Task RunAsRootTypeTest<T>(List<T> numbers)
+        {
+            foreach (T number in numbers)
+            {
+                string numberAsString = GetNumberAsString(number);
+                string json = $"{numberAsString}";
+                string jsonWithNumberAsString = @$"""{numberAsString}""";
+                await PerformAsRootTypeSerialization(number, json, jsonWithNumberAsString);
+            }
+        }
+
+        private static string GetNumberAsString<T>(T number)
+        {
+            return number switch
+            {
+                double @double => @double.ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture),
+                float @float => @float.ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture),
+                decimal @decimal => @decimal.ToString(CultureInfo.InvariantCulture),
+                _ => number.ToString()
+            };
+        }
+
+        private async Task PerformAsRootTypeSerialization<T>(T number, string jsonWithNumberAsNumber, string jsonWithNumberAsString)
+        {
+            // Option: read from string
+
+            // Deserialize
+            Assert.Equal(number, await Serializer.DeserializeWrapper<T>(jsonWithNumberAsNumber, s_optionReadFromStr));
+            Assert.Equal(number, await Serializer.DeserializeWrapper<T>(jsonWithNumberAsString, s_optionReadFromStr));
+
+            // Serialize
+            Assert.Equal(jsonWithNumberAsNumber, await Serializer.SerializeWrapper(number, s_optionReadFromStr));
+
+            // Option: write as string
+
+            // Deserialize
+            Assert.Equal(number, await Serializer.DeserializeWrapper<T>(jsonWithNumberAsNumber, s_optionWriteAsStr));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<T>(jsonWithNumberAsString, s_optionWriteAsStr));
+
+            // Serialize
+            Assert.Equal(jsonWithNumberAsString, await Serializer.SerializeWrapper(number, s_optionWriteAsStr));
+
+            // Option: read and write from/to string
+
+            // Deserialize
+            Assert.Equal(number, await Serializer.DeserializeWrapper<T>(jsonWithNumberAsNumber, s_optionReadAndWriteFromStr));
+            Assert.Equal(number, await Serializer.DeserializeWrapper<T>(jsonWithNumberAsString, s_optionReadAndWriteFromStr));
+
+            // Serialize
+            Assert.Equal(jsonWithNumberAsString, await Serializer.SerializeWrapper(number, s_optionReadAndWriteFromStr));
+        }
+
+        [Fact]
+        public async Task Number_AsBoxed_RootType()
+        {
+            string numberAsString = @"""2""";
+
+            int @int = 2;
+            float @float = 2;
+            int? nullableInt = 2;
+            float? nullableFloat = 2;
+
+            Assert.Equal(numberAsString, await Serializer.SerializeWrapper((object)@int, s_optionReadAndWriteFromStr));
+            Assert.Equal(numberAsString, await Serializer.SerializeWrapper((object)@float, s_optionReadAndWriteFromStr));
+            Assert.Equal(numberAsString, await Serializer.SerializeWrapper((object)nullableInt, s_optionReadAndWriteFromStr));
+            Assert.Equal(numberAsString, await Serializer.SerializeWrapper((object)nullableFloat, s_optionReadAndWriteFromStr));
+
+            Assert.Equal(2, (int)await Serializer.DeserializeWrapper(numberAsString, typeof(int), s_optionReadAndWriteFromStr));
+            Assert.Equal(2, (float)await Serializer.DeserializeWrapper(numberAsString, typeof(float), s_optionReadAndWriteFromStr));
+            Assert.Equal(2, (int?)await Serializer.DeserializeWrapper(numberAsString, typeof(int?), s_optionReadAndWriteFromStr));
+            Assert.Equal(2, (float?)await Serializer.DeserializeWrapper(numberAsString, typeof(float?), s_optionReadAndWriteFromStr));
+        }
+
+        [Fact]
+        public async Task Number_AsBoxed_Property()
+        {
+            int @int = 1;
+            float? nullableFloat = 2;
+
+            string expected = @"{""MyInt"":""1"",""MyNullableFloat"":""2""}";
+
+            var obj = new Class_With_BoxedNumbers
+            {
+                MyInt = @int,
+                MyNullableFloat = nullableFloat
+            };
+
+            string serialized = await Serializer.SerializeWrapper(obj);
+            JsonTestHelper.AssertJsonEqual(expected, serialized);
+
+            obj = await Serializer.DeserializeWrapper<Class_With_BoxedNumbers>(serialized);
+
+            JsonElement el = Assert.IsType<JsonElement>(obj.MyInt);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal("1", el.GetString());
+
+            el = Assert.IsType<JsonElement>(obj.MyNullableFloat);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal("2", el.GetString());
+        }
+
+        public class Class_With_BoxedNumbers
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public object MyInt { get; set; }
+
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public object MyNullableFloat { get; set; }
+        }
+
+        [Fact]
+        public async Task Number_AsBoxed_CollectionRootType_Element()
+        {
+            int @int = 1;
+            float? nullableFloat = 2;
+
+            string expected = @"[""1""]";
+
+            var obj = new List<object> { @int };
+            string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr);
+            Assert.Equal(expected, serialized);
+
+            obj = await Serializer.DeserializeWrapper<List<object>>(serialized, s_optionReadAndWriteFromStr);
+
+            JsonElement el = Assert.IsType<JsonElement>(obj[0]);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal("1", el.GetString());
+
+            expected = @"[""2""]";
+
+            IList obj2 = new object[] { nullableFloat };
+            serialized = await Serializer.SerializeWrapper(obj2, s_optionReadAndWriteFromStr);
+            Assert.Equal(expected, serialized);
+
+            obj2 = await Serializer.DeserializeWrapper<IList>(serialized, s_optionReadAndWriteFromStr);
+
+            el = Assert.IsType<JsonElement>(obj2[0]);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal("2", el.GetString());
+        }
+
+        [Fact]
+        public async Task Number_AsBoxed_CollectionProperty_Element()
+        {
+            int @int = 2;
+            float? nullableFloat = 2;
+
+            string expected = @"{""MyInts"":[""2""],""MyNullableFloats"":[""2""]}";
+
+            var obj = new Class_With_ListsOfBoxedNumbers
+            {
+                MyInts = new List<object> { @int },
+                MyNullableFloats = new object[] { nullableFloat }
+            };
+
+            string serialized = await Serializer.SerializeWrapper(obj);
+            JsonTestHelper.AssertJsonEqual(expected, serialized);
+
+            obj = await Serializer.DeserializeWrapper<Class_With_ListsOfBoxedNumbers>(serialized);
+
+            JsonElement el = Assert.IsType<JsonElement>(obj.MyInts[0]);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal("2", el.GetString());
+
+            el = Assert.IsType<JsonElement>(obj.MyNullableFloats[0]);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal("2", el.GetString());
+        }
+
+        public class Class_With_ListsOfBoxedNumbers
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public List<object> MyInts { get; set; }
+
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public IList MyNullableFloats { get; set; }
+        }
+
+        [Fact]
+        public async Task NonNumber_AsBoxed_Property()
+        {
+            DateTime dateTime = DateTimeTestHelpers.FixedDateTimeValue;
+            Guid? nullableGuid = Guid.NewGuid();
+
+            string expected = @$"{{""MyDateTime"":{await Serializer.SerializeWrapper(dateTime)},""MyNullableGuid"":{await Serializer.SerializeWrapper(nullableGuid)}}}";
+
+            var obj = new Class_With_BoxedNonNumbers
+            {
+                MyDateTime = dateTime,
+                MyNullableGuid = nullableGuid
+            };
+
+            string serialized = await Serializer.SerializeWrapper(obj);
+            JsonTestHelper.AssertJsonEqual(expected, serialized);
+
+            obj = await Serializer.DeserializeWrapper<Class_With_BoxedNonNumbers>(serialized);
+
+            JsonElement el = Assert.IsType<JsonElement>(obj.MyDateTime);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal(dateTime, el.GetDateTime());
+
+            el = Assert.IsType<JsonElement>(obj.MyNullableGuid);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal(nullableGuid.Value, el.GetGuid());
+        }
+
+        public class Class_With_BoxedNonNumbers
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public object MyDateTime { get; set; }
+
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public object MyNullableGuid { get; set; }
+        }
+
+        [Fact]
+        public async Task NonNumber_AsBoxed_CollectionRootType_Element()
+        {
+            DateTime dateTime = DateTimeTestHelpers.FixedDateTimeValue;
+            Guid? nullableGuid = Guid.NewGuid();
+
+            string expected = @$"[{await Serializer.SerializeWrapper(dateTime)}]";
+
+            var obj = new List<object> { dateTime };
+            string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr);
+            Assert.Equal(expected, serialized);
+
+            obj = await Serializer.DeserializeWrapper<List<object>>(serialized, s_optionReadAndWriteFromStr);
+
+            JsonElement el = Assert.IsType<JsonElement>(obj[0]);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal(dateTime, el.GetDateTime());
+
+            expected = @$"[{await Serializer.SerializeWrapper(nullableGuid)}]";
+
+            IList obj2 = new object[] { nullableGuid };
+            serialized = await Serializer.SerializeWrapper(obj2, s_optionReadAndWriteFromStr);
+            Assert.Equal(expected, serialized);
+
+            obj2 = await Serializer.DeserializeWrapper<IList>(serialized, s_optionReadAndWriteFromStr);
+
+            el = Assert.IsType<JsonElement>(obj2[0]);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal(nullableGuid.Value, el.GetGuid());
+        }
+
+        [Fact]
+        public async Task NonNumber_AsBoxed_CollectionProperty_Element()
+        {
+            DateTime dateTime = DateTimeTestHelpers.FixedDateTimeValue;
+            Guid? nullableGuid = Guid.NewGuid();
+
+            string expected = @$"{{""MyDateTimes"":[{await Serializer.SerializeWrapper(dateTime)}],""MyNullableGuids"":[{await Serializer.SerializeWrapper(nullableGuid)}]}}";
+
+            var obj = new Class_With_ListsOfBoxedNonNumbers
+            {
+                MyDateTimes = new List<object> { dateTime },
+                MyNullableGuids = new object[] { nullableGuid }
+            };
+
+            string serialized = await Serializer.SerializeWrapper(obj);
+            JsonTestHelper.AssertJsonEqual(expected, serialized);
+
+            obj = await Serializer.DeserializeWrapper<Class_With_ListsOfBoxedNonNumbers>(serialized);
+
+            JsonElement el = Assert.IsType<JsonElement>(obj.MyDateTimes[0]);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal(dateTime, el.GetDateTime());
+
+            el = Assert.IsType<JsonElement>(obj.MyNullableGuids[0]);
+            Assert.Equal(JsonValueKind.String, el.ValueKind);
+            Assert.Equal(nullableGuid, el.GetGuid());
+        }
+
+        public class Class_With_ListsOfBoxedNonNumbers
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public List<object> MyDateTimes { get; set; }
+
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public IList MyNullableGuids { get; set; }
+        }
+
+        [Fact]
+        public async Task Number_AsCollectionElement_RoundTrip()
+        {
+            await RunAsCollectionElementTest(JsonNumberTestData.Bytes);
+            await RunAsCollectionElementTest(JsonNumberTestData.SBytes);
+            await RunAsCollectionElementTest(JsonNumberTestData.Shorts);
+            await RunAsCollectionElementTest(JsonNumberTestData.Ints);
+            await RunAsCollectionElementTest(JsonNumberTestData.Longs);
+            await RunAsCollectionElementTest(JsonNumberTestData.UShorts);
+            await RunAsCollectionElementTest(JsonNumberTestData.UInts);
+            await RunAsCollectionElementTest(JsonNumberTestData.ULongs);
+            await RunAsCollectionElementTest(JsonNumberTestData.Floats);
+            await RunAsCollectionElementTest(JsonNumberTestData.Doubles);
+            await RunAsCollectionElementTest(JsonNumberTestData.Decimals);
+
+            // https://github.com/dotnet/runtime/issues/66220
+            if (!PlatformDetection.IsAppleMobile)
+            {
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableBytes);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableSBytes);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableShorts);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableInts);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableLongs);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableUShorts);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableUInts);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableULongs);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableFloats);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableDoubles);
+                await RunAsCollectionElementTest(JsonNumberTestData.NullableDecimals);
+            }
+        }
+
+        private async Task RunAsCollectionElementTest<T>(List<T> numbers)
+        {
+            StringBuilder jsonBuilder_NumbersAsNumbers = new StringBuilder();
+            StringBuilder jsonBuilder_NumbersAsStrings = new StringBuilder();
+            StringBuilder jsonBuilder_NumbersAsNumbersAndStrings = new StringBuilder();
+            StringBuilder jsonBuilder_NumbersAsNumbersAndStrings_Alternate = new StringBuilder();
+            bool asNumber = false;
+
+            jsonBuilder_NumbersAsNumbers.Append("[");
+            jsonBuilder_NumbersAsStrings.Append("[");
+            jsonBuilder_NumbersAsNumbersAndStrings.Append("[");
+            jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Append("[");
+
+            foreach (T number in numbers)
+            {
+                string numberAsString = GetNumberAsString(number);
+
+                string jsonWithNumberAsString = @$"""{numberAsString}""";
+
+                jsonBuilder_NumbersAsNumbers.Append($"{numberAsString},");
+                jsonBuilder_NumbersAsStrings.Append($"{jsonWithNumberAsString},");
+                jsonBuilder_NumbersAsNumbersAndStrings.Append(asNumber
+                    ? $"{numberAsString},"
+                    : $"{jsonWithNumberAsString},");
+                jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Append(!asNumber
+                    ? $"{numberAsString},"
+                    : $"{jsonWithNumberAsString},");
+
+                asNumber = !asNumber;
+            }
+
+            jsonBuilder_NumbersAsNumbers.Remove(jsonBuilder_NumbersAsNumbers.Length - 1, 1);
+            jsonBuilder_NumbersAsStrings.Remove(jsonBuilder_NumbersAsStrings.Length - 1, 1);
+            jsonBuilder_NumbersAsNumbersAndStrings.Remove(jsonBuilder_NumbersAsNumbersAndStrings.Length - 1, 1);
+            jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Remove(jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Length - 1, 1);
+
+            jsonBuilder_NumbersAsNumbers.Append("]");
+            jsonBuilder_NumbersAsStrings.Append("]");
+            jsonBuilder_NumbersAsNumbersAndStrings.Append("]");
+            jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Append("]");
+
+            string jsonNumbersAsStrings = jsonBuilder_NumbersAsStrings.ToString();
+
+            await PerformAsCollectionElementSerialization(
+                numbers,
+                jsonBuilder_NumbersAsNumbers.ToString(),
+                jsonNumbersAsStrings,
+                jsonBuilder_NumbersAsNumbersAndStrings.ToString(),
+                jsonBuilder_NumbersAsNumbersAndStrings_Alternate.ToString());
+
+            // Reflection based tests for every collection type.
+            await RunAllCollectionsRoundTripTest<T>(jsonNumbersAsStrings);
+        }
+
+        private async Task PerformAsCollectionElementSerialization<T>(
+            List<T> numbers,
+            string json_NumbersAsNumbers,
+            string json_NumbersAsStrings,
+            string json_NumbersAsNumbersAndStrings,
+            string json_NumbersAsNumbersAndStrings_Alternate)
+        {
+            List<T> deserialized;
+
+            // Option: read from string
+
+            // Deserialize
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbers, s_optionReadFromStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsStrings, s_optionReadFromStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbersAndStrings, s_optionReadFromStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbersAndStrings_Alternate, s_optionReadFromStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            // Serialize
+            Assert.Equal(json_NumbersAsNumbers, await Serializer.SerializeWrapper(numbers, s_optionReadFromStr));
+
+            // Option: write as string
+
+            // Deserialize
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbers, s_optionWriteAsStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsStrings, s_optionWriteAsStr));
+
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbersAndStrings, s_optionWriteAsStr));
+
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbersAndStrings_Alternate, s_optionWriteAsStr));
+
+            // Serialize
+            Assert.Equal(json_NumbersAsStrings, await Serializer.SerializeWrapper(numbers, s_optionWriteAsStr));
+
+            // Option: read and write from/to string
+
+            // Deserialize
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbers, s_optionReadAndWriteFromStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsStrings, s_optionReadAndWriteFromStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbersAndStrings, s_optionReadAndWriteFromStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            deserialized = await Serializer.DeserializeWrapper<List<T>>(json_NumbersAsNumbersAndStrings_Alternate, s_optionReadAndWriteFromStr);
+            AssertIEnumerableEqual(numbers, deserialized);
+
+            // Serialize
+            Assert.Equal(json_NumbersAsStrings, await Serializer.SerializeWrapper(numbers, s_optionReadAndWriteFromStr));
+        }
+
+        private void AssertIEnumerableEqual<T>(IEnumerable<T> list1, IEnumerable<T> list2)
+        {
+            IEnumerator<T> enumerator1 = list1.GetEnumerator();
+            IEnumerator<T> enumerator2 = list2.GetEnumerator();
+
+            while (enumerator1.MoveNext())
+            {
+                enumerator2.MoveNext();
+                Assert.Equal(enumerator1.Current, enumerator2.Current);
+            }
+
+            Assert.False(enumerator2.MoveNext());
+        }
+
+        private async Task RunAllCollectionsRoundTripTest<T>(string json)
+        {
+            foreach (Type type in CollectionTestTypes.DeserializableGenericEnumerableTypes<T>())
+            {
+                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(HashSet<>))
+                {
+                    HashSet<T> obj1 = (HashSet<T>)await Serializer.DeserializeWrapper(json, type, s_optionReadAndWriteFromStr);
+                    string serialized = await Serializer.SerializeWrapper(obj1, s_optionReadAndWriteFromStr);
+
+                    HashSet<T> obj2 = (HashSet<T>)await Serializer.DeserializeWrapper(serialized, type, s_optionReadAndWriteFromStr);
+
+                    Assert.Equal(obj1.Count, obj2.Count);
+                    foreach (T element in obj1)
+                    {
+                        Assert.True(obj2.Contains(element));
+                    }
+                }
+                else if (type != typeof(byte[]))
+                {
+                    object obj = await Serializer.DeserializeWrapper(json, type, s_optionReadAndWriteFromStr);
+                    string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr);
+                    Assert.Equal(json, serialized);
+                }
+            }
+
+            foreach (Type type in CollectionTestTypes.DeserializableNonGenericEnumerableTypes())
+            {
+                // Deserialized as collection of JsonElements.
+                object obj = await Serializer.DeserializeWrapper(json, type, s_optionReadAndWriteFromStr);
+                // Serialized as strings with escaping.
+                string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr);
+
+                // Ensure escaped values were serialized accurately
+                List<T> list = await Serializer.DeserializeWrapper<List<T>>(serialized, s_optionReadAndWriteFromStr);
+                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);
+            }
+        }
+
+        [Fact]
+        public async Task Number_AsDictionaryElement_RoundTrip()
+        {
+            var dict = new Dictionary<int, float>();
+            for (int i = 0; i < 10; i++)
+            {
+                dict[JsonNumberTestData.Ints[i]] = JsonNumberTestData.Floats[i];
+            }
+
+            // Serialize
+            string serialized = await Serializer.SerializeWrapper(dict, s_optionReadAndWriteFromStr);
+            AssertDictionaryElements_StringValues(serialized);
+
+            // Deserialize
+            dict = await Serializer.DeserializeWrapper<Dictionary<int, float>>(serialized, s_optionReadAndWriteFromStr);
+
+            // Test roundtrip
+            JsonTestHelper.AssertJsonEqual(serialized, await Serializer.SerializeWrapper(dict, s_optionReadAndWriteFromStr));
+        }
+
+        private void AssertDictionaryElements_StringValues(string serialized)
+        {
+            Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(serialized));
+            reader.Read();
+            while (reader.Read())
+            {
+                if (reader.TokenType == JsonTokenType.EndObject)
+                {
+                    break;
+                }
+                else if (reader.TokenType == JsonTokenType.String)
+                {
+                    Assert.True(reader.ValueSpan.IndexOf((byte)'\\') == -1);
+                }
+                else
+                {
+                    Assert.Equal(JsonTokenType.PropertyName, reader.TokenType);
+                }
+            }
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/39674", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))]
+        [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/45464", ~RuntimeConfiguration.Release)]
+        public async Task DictionariesRoundTrip()
+        {
+            await RunAllDictionariessRoundTripTest(JsonNumberTestData.ULongs);
+            await RunAllDictionariessRoundTripTest(JsonNumberTestData.Floats);
+            await RunAllDictionariessRoundTripTest(JsonNumberTestData.Doubles);
+        }
+
+        private async Task RunAllDictionariessRoundTripTest<T>(List<T> numbers)
+        {
+            StringBuilder jsonBuilder_NumbersAsStrings = new StringBuilder();
+
+            jsonBuilder_NumbersAsStrings.Append("{");
+
+            foreach (T number in numbers)
+            {
+                string numberAsString = GetNumberAsString(number);
+                string jsonWithNumberAsString = @$"""{numberAsString}""";
+
+                jsonBuilder_NumbersAsStrings.Append($"{jsonWithNumberAsString}:");
+                jsonBuilder_NumbersAsStrings.Append($"{jsonWithNumberAsString},");
+            }
+
+            jsonBuilder_NumbersAsStrings.Remove(jsonBuilder_NumbersAsStrings.Length - 1, 1);
+            jsonBuilder_NumbersAsStrings.Append("}");
+
+            string jsonNumbersAsStrings = jsonBuilder_NumbersAsStrings.ToString();
+
+            foreach (Type type in CollectionTestTypes.DeserializableDictionaryTypes<string, T>())
+            {
+                object obj = await Serializer.DeserializeWrapper(jsonNumbersAsStrings, type, s_optionReadAndWriteFromStr);
+                JsonTestHelper.AssertJsonEqual(jsonNumbersAsStrings, await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr));
+            }
+
+            foreach (Type type in CollectionTestTypes.DeserializableNonGenericDictionaryTypes())
+            {
+                Dictionary<T, T> dict = await Serializer.DeserializeWrapper<Dictionary<T, T>>(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);
+            }
+        }
+
+        [Fact]
+        public async Task Number_AsPropertyValue_RoundTrip()
+        {
+            var obj = new Class_With_NullableUInt64_And_Float()
+            {
+                NullableUInt64Number = JsonNumberTestData.NullableULongs.LastOrDefault(),
+                FloatNumbers = JsonNumberTestData.Floats
+            };
+
+            // Serialize
+            string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr);
+
+            // Deserialize
+            obj = await Serializer.DeserializeWrapper<Class_With_NullableUInt64_And_Float>(serialized, s_optionReadAndWriteFromStr);
+
+            // Test roundtrip
+            JsonTestHelper.AssertJsonEqual(serialized, await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr));
+        }
+
+        public class Class_With_NullableUInt64_And_Float
+        {
+            public ulong? NullableUInt64Number { get; set; }
+            [JsonInclude]
+            public List<float> FloatNumbers;
+        }
+
+        [Fact]
+        public async Task Number_AsKeyValuePairValue_RoundTrip()
+        {
+            var obj = new KeyValuePair<ulong?, List<float>>(JsonNumberTestData.NullableULongs.LastOrDefault(), JsonNumberTestData.Floats);
+
+            // Serialize
+            string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr);
+
+            // Deserialize
+            obj = await Serializer.DeserializeWrapper<KeyValuePair<ulong?, List<float>>>(serialized, s_optionReadAndWriteFromStr);
+
+            // Test roundtrip
+            JsonTestHelper.AssertJsonEqual(serialized, await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr));
+        }
+
+        [Fact]
+        public async Task Number_AsObjectWithParameterizedCtor_RoundTrip()
+        {
+            var obj = new MyClassWithNumbers(JsonNumberTestData.NullableULongs.LastOrDefault(), JsonNumberTestData.Floats);
+
+            // Serialize
+            string serialized = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr);
+
+            // Deserialize
+            obj = await Serializer.DeserializeWrapper<MyClassWithNumbers>(serialized, s_optionReadAndWriteFromStr);
+
+            // Test roundtrip
+            JsonTestHelper.AssertJsonEqual(serialized, await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr));
+        }
+
+        public class MyClassWithNumbers
+        {
+            public ulong? Ulong { get; }
+            public List<float> ListOfFloats { get; }
+
+            public MyClassWithNumbers(ulong? @ulong, List<float> listOfFloats)
+            {
+                Ulong = @ulong;
+                ListOfFloats = listOfFloats;
+            }
+        }
+
+        [Fact]
+        public async Task Number_AsObjectWithParameterizedCtor_PropHasAttribute()
+        {
+            string json = @"{""ListOfFloats"":[""1""]}";
+            // Strict handling on property overrides loose global policy.
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<MyClassWithNumbers_PropsHasAttribute>(json, s_optionReadFromStr));
+
+            // Serialize
+            json = @"{""ListOfFloats"":[1]}";
+            MyClassWithNumbers_PropsHasAttribute obj = await Serializer.DeserializeWrapper<MyClassWithNumbers_PropsHasAttribute>(json);
+
+            // Number serialized as JSON number due to strict handling on property which overrides loose global policy.
+            Assert.Equal(json, await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr));
+        }
+
+        public class MyClassWithNumbers_PropsHasAttribute
+        {
+            [JsonNumberHandling(JsonNumberHandling.Strict)]
+            public List<float> ListOfFloats { get; }
+
+            public MyClassWithNumbers_PropsHasAttribute(List<float> listOfFloats)
+            {
+                ListOfFloats = listOfFloats;
+            }
+        }
+
+        [Fact]
+        public async Task FloatingPointConstants_Pass()
+        {
+            // Valid values
+            await PerformFloatingPointSerialization("NaN");
+            await PerformFloatingPointSerialization("Infinity");
+            await PerformFloatingPointSerialization("-Infinity");
+
+            await PerformFloatingPointSerialization("\u004EaN"); // NaN
+            await PerformFloatingPointSerialization("Inf\u0069ni\u0074y"); // Infinity
+            await PerformFloatingPointSerialization("\u002DInf\u0069nity"); // -Infinity
+
+            async Task PerformFloatingPointSerialization(string testString)
+            {
+                string testStringAsJson = $@"""{testString}""";
+                string testJson = @$"{{""FloatNumber"":{testStringAsJson},""DoubleNumber"":{testStringAsJson}}}";
+
+                StructWithNumbers obj;
+                switch (testString)
+                {
+                    case "NaN":
+                        obj = await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionsAllowFloatConstants);
+                        Assert.Equal(float.NaN, obj.FloatNumber);
+                        Assert.Equal(double.NaN, obj.DoubleNumber);
+
+                        obj = await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionReadFromStr);
+                        Assert.Equal(float.NaN, obj.FloatNumber);
+                        Assert.Equal(double.NaN, obj.DoubleNumber);
+                        break;
+                    case "Infinity":
+                        obj = await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionsAllowFloatConstants);
+                        Assert.Equal(float.PositiveInfinity, obj.FloatNumber);
+                        Assert.Equal(double.PositiveInfinity, obj.DoubleNumber);
+
+                        obj = await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionReadFromStr);
+                        Assert.Equal(float.PositiveInfinity, obj.FloatNumber);
+                        Assert.Equal(double.PositiveInfinity, obj.DoubleNumber);
+                        break;
+                    case "-Infinity":
+                        obj = await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionsAllowFloatConstants);
+                        Assert.Equal(float.NegativeInfinity, obj.FloatNumber);
+                        Assert.Equal(double.NegativeInfinity, obj.DoubleNumber);
+
+                        obj = await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionReadFromStr);
+                        Assert.Equal(float.NegativeInfinity, obj.FloatNumber);
+                        Assert.Equal(double.NegativeInfinity, obj.DoubleNumber);
+                        break;
+                    default:
+                        await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionsAllowFloatConstants));
+                        return;
+                }
+
+                JsonTestHelper.AssertJsonEqual(testJson, await Serializer.SerializeWrapper(obj, s_optionsAllowFloatConstants));
+                JsonTestHelper.AssertJsonEqual(testJson, await Serializer.SerializeWrapper(obj, s_optionWriteAsStr));
+            }
+        }
+
+        [Theory]
+        [InlineData("naN")]
+        [InlineData("Nan")]
+        [InlineData("NAN")]
+        [InlineData("+Infinity")]
+        [InlineData("+infinity")]
+        [InlineData("infinity")]
+        [InlineData("infinitY")]
+        [InlineData("INFINITY")]
+        [InlineData("+INFINITY")]
+        [InlineData("-infinity")]
+        [InlineData("-infinitY")]
+        [InlineData("-INFINITY")]
+        [InlineData(" NaN")]
+        [InlineData("NaN ")]
+        [InlineData(" Infinity")]
+        [InlineData(" -Infinity")]
+        [InlineData("Infinity ")]
+        [InlineData("-Infinity ")]
+        [InlineData("a-Infinity")]
+        [InlineData("NaNa")]
+        [InlineData("Infinitya")]
+        [InlineData("-Infinitya")]
+#pragma warning disable xUnit1025 // Theory method 'FloatingPointConstants_Fail' on test class 'NumberHandlingTests' has InlineData duplicate(s)
+        [InlineData("\u006EaN")] // "naN"
+        [InlineData("\u0020Inf\u0069ni\u0074y")] // " Infinity"
+        [InlineData("\u002BInf\u0069nity")] // "+Infinity"
+#pragma warning restore xUnit1025
+        public async Task FloatingPointConstants_Fail(string testString)
+        {
+            string testStringAsJson = $@"""{testString}""";
+
+            string testJson = @$"{{""FloatNumber"":{testStringAsJson}}}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionReadFromStr));
+
+            testJson = @$"{{""DoubleNumber"":{testStringAsJson}}}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<StructWithNumbers>(testJson, s_optionReadFromStr));
+        }
+
+        [Fact]
+        public async Task AllowFloatingPointConstants_WriteAsNumber_IfNotConstant()
+        {
+            float @float = 1;
+            // Not written as "1"
+            Assert.Equal("1", await Serializer.SerializeWrapper(@float, s_optionsAllowFloatConstants));
+
+            double @double = 1;
+            // Not written as "1"
+            Assert.Equal("1", await Serializer.SerializeWrapper(@double, s_optionsAllowFloatConstants));
+        }
+
+        [Theory]
+        [InlineData("NaN")]
+        [InlineData("Infinity")]
+        [InlineData("-Infinity")]
+        public async Task Unquoted_FloatingPointConstants_Read_Fail(string testString)
+        {
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<float>(testString, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<double?>(testString, s_optionReadFromStr));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<double>(testString, s_optionReadFromStrAllowFloatConstants));
+        }
+
+        public struct StructWithNumbers
+        {
+            public float FloatNumber { get; set; }
+            public double DoubleNumber { get; set; }
+        }
+
+        [Fact]
+        public async Task ReadFromString_AllowFloatingPoint()
+        {
+            string json = @"{""IntNumber"":""1"",""FloatNumber"":""NaN""}";
+            ClassWithNumbers obj = await Serializer.DeserializeWrapper<ClassWithNumbers>(json, s_optionReadFromStrAllowFloatConstants);
+
+            Assert.Equal(1, obj.IntNumber);
+            Assert.Equal(float.NaN, obj.FloatNumber);
+
+            JsonTestHelper.AssertJsonEqual(@"{""IntNumber"":1,""FloatNumber"":""NaN""}", await Serializer.SerializeWrapper(obj, s_optionReadFromStrAllowFloatConstants));
+        }
+
+        [Fact]
+        public async Task WriteAsString_AllowFloatingPoint()
+        {
+            string json = @"{""IntNumber"":""1"",""FloatNumber"":""NaN""}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ClassWithNumbers>(json, s_optionWriteAsStrAllowFloatConstants));
+
+            var obj = new ClassWithNumbers
+            {
+                IntNumber = 1,
+                FloatNumber = float.NaN
+            };
+
+            JsonTestHelper.AssertJsonEqual(json, await Serializer.SerializeWrapper(obj, s_optionWriteAsStrAllowFloatConstants));
+        }
+
+        public class ClassWithNumbers
+        {
+            public int IntNumber { get; set; }
+            public float FloatNumber { get; set; }
+        }
+
+        [Fact]
+        public async Task FloatingPointConstants_IncompatibleNumber()
+        {
+            await AssertFloatingPointIncompatible_Fails<byte>();
+            await AssertFloatingPointIncompatible_Fails<sbyte>();
+            await AssertFloatingPointIncompatible_Fails<short>();
+            await AssertFloatingPointIncompatible_Fails<int>();
+            await AssertFloatingPointIncompatible_Fails<long>();
+            await AssertFloatingPointIncompatible_Fails<ushort>();
+            await AssertFloatingPointIncompatible_Fails<uint>();
+            await AssertFloatingPointIncompatible_Fails<ulong>();
+            await AssertFloatingPointIncompatible_Fails<decimal>();
+            await AssertFloatingPointIncompatible_Fails<byte?>();
+            await AssertFloatingPointIncompatible_Fails<sbyte?>();
+            await AssertFloatingPointIncompatible_Fails<short?>();
+            await AssertFloatingPointIncompatible_Fails<int?>();
+            await AssertFloatingPointIncompatible_Fails<long?>();
+            await AssertFloatingPointIncompatible_Fails<ushort?>();
+            await AssertFloatingPointIncompatible_Fails<uint?>();
+            await AssertFloatingPointIncompatible_Fails<ulong?>();
+            await AssertFloatingPointIncompatible_Fails<decimal?>();
+        }
+
+        private async Task AssertFloatingPointIncompatible_Fails<T>()
+        {
+            string[] testCases = new[]
+            {
+                @"""NaN""",
+                @"""Infinity""",
+                @"""-Infinity""",
+            };
+
+            foreach (string test in testCases)
+            {
+                await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<T>(test, s_optionReadFromStrAllowFloatConstants));
+            }
+        }
+
+        [Fact]
+        public async Task UnsupportedFormats()
+        {
+            await AssertUnsupportedFormatThrows<byte>();
+            await AssertUnsupportedFormatThrows<sbyte>();
+            await AssertUnsupportedFormatThrows<short>();
+            await AssertUnsupportedFormatThrows<int>();
+            await AssertUnsupportedFormatThrows<long>();
+            await AssertUnsupportedFormatThrows<ushort>();
+            await AssertUnsupportedFormatThrows<uint>();
+            await AssertUnsupportedFormatThrows<ulong>();
+            await AssertUnsupportedFormatThrows<float>();
+            await AssertUnsupportedFormatThrows<decimal>();
+            await AssertUnsupportedFormatThrows<byte?>();
+            await AssertUnsupportedFormatThrows<sbyte?>();
+            await AssertUnsupportedFormatThrows<short?>();
+            await AssertUnsupportedFormatThrows<int?>();
+            await AssertUnsupportedFormatThrows<long?>();
+            await AssertUnsupportedFormatThrows<ushort?>();
+            await AssertUnsupportedFormatThrows<uint?>();
+            await AssertUnsupportedFormatThrows<ulong?>();
+            await AssertUnsupportedFormatThrows<float?>();
+            await AssertUnsupportedFormatThrows<decimal?>();
+        }
+
+        private async Task AssertUnsupportedFormatThrows<T>()
+        {
+            string[] testCases = new[]
+            {
+                "$123.46", // Currency
+                "100.00 %", // Percent
+                 "1234,57", // Fixed point
+                 "00FF", // Hexadecimal
+            };
+
+            foreach (string test in testCases)
+            {
+                await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<T>(test, s_optionReadFromStr));
+            }
+        }
+
+        [Fact]
+        public async Task EscapingTest()
+        {
+            // Cause all characters to be escaped.
+            var encoderSettings = new TextEncoderSettings();
+            encoderSettings.ForbidCharacters('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '+', '-', 'e', 'E');
+
+            JavaScriptEncoder encoder = JavaScriptEncoder.Create(encoderSettings);
+            var options = new JsonSerializerOptions(s_optionReadAndWriteFromStr)
+            {
+                Encoder = encoder
+            };
+
+            await PerformEscapingTest(JsonNumberTestData.Bytes, options);
+            await PerformEscapingTest(JsonNumberTestData.SBytes, options);
+            await PerformEscapingTest(JsonNumberTestData.Shorts, options);
+            await PerformEscapingTest(JsonNumberTestData.Ints, options);
+            await PerformEscapingTest(JsonNumberTestData.Longs, options);
+            await PerformEscapingTest(JsonNumberTestData.UShorts, options);
+            await PerformEscapingTest(JsonNumberTestData.UInts, options);
+            await PerformEscapingTest(JsonNumberTestData.ULongs, options);
+            await PerformEscapingTest(JsonNumberTestData.Floats, options);
+            await PerformEscapingTest(JsonNumberTestData.Doubles, options);
+            await PerformEscapingTest(JsonNumberTestData.Decimals, options);
+        }
+
+        private async Task PerformEscapingTest<T>(List<T> numbers, JsonSerializerOptions options)
+        {
+            // All input characters are escaped
+            IEnumerable<string> numbersAsStrings = numbers.Select(num => GetNumberAsString(num));
+            string input = await Serializer.SerializeWrapper(numbersAsStrings, options);
+            AssertListNumbersEscaped(input);
+
+            // Unescaping works
+            List<T> deserialized = await Serializer.DeserializeWrapper<List<T>>(input, options);
+            Assert.Equal(numbers.Count, deserialized.Count);
+            for (int i = 0; i < numbers.Count; i++)
+            {
+                Assert.Equal(numbers[i], deserialized[i]);
+            }
+
+            // Every number is written as a string, and custom escaping is not honored.
+            string serialized = await Serializer.SerializeWrapper(deserialized, options);
+            AssertListNumbersUnescaped(serialized);
+        }
+
+        private void AssertListNumbersEscaped(string json)
+        {
+            Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json));
+            reader.Read();
+            while (reader.Read())
+            {
+                if (reader.TokenType == JsonTokenType.EndArray)
+                {
+                    break;
+                }
+                else
+                {
+                    Assert.Equal(JsonTokenType.String, reader.TokenType);
+                    Assert.True(reader.ValueSpan.IndexOf((byte)'\\') != -1);
+                }
+            }
+        }
+
+        private void AssertListNumbersUnescaped(string json)
+        {
+            Utf8JsonReader reader = new(Encoding.UTF8.GetBytes(json));
+            reader.Read();
+            while (reader.Read())
+            {
+                if (reader.TokenType == JsonTokenType.EndArray)
+                {
+                    break;
+                }
+                else
+                {
+                    Assert.Equal(JsonTokenType.String, reader.TokenType);
+                    Assert.True(reader.ValueSpan.IndexOf((byte)'\\') == -1);
+                }
+            }
+        }
+
+        [Fact]
+        public async Task Number_RoundtripNull()
+        {
+            await Perform_Number_RoundTripNull_Test<byte>();
+            await Perform_Number_RoundTripNull_Test<sbyte>();
+            await Perform_Number_RoundTripNull_Test<short>();
+            await Perform_Number_RoundTripNull_Test<int>();
+            await Perform_Number_RoundTripNull_Test<long>();
+            await Perform_Number_RoundTripNull_Test<ushort>();
+            await Perform_Number_RoundTripNull_Test<uint>();
+            await Perform_Number_RoundTripNull_Test<ulong>();
+            await Perform_Number_RoundTripNull_Test<float>();
+            await Perform_Number_RoundTripNull_Test<decimal>();
+        }
+
+        private async Task Perform_Number_RoundTripNull_Test<T>()
+        {
+            string nullAsJson = "null";
+            string nullAsQuotedJson = $@"""{nullAsJson}""";
+
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<T>(nullAsJson, s_optionReadAndWriteFromStr));
+            Assert.Equal("0", await Serializer.SerializeWrapper(default(T)));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<T>(nullAsQuotedJson, s_optionReadAndWriteFromStr));
+        }
+
+        [Fact]
+        public async Task NullableNumber_RoundtripNull()
+        {
+            await Perform_NullableNumber_RoundTripNull_Test<byte?>();
+            await Perform_NullableNumber_RoundTripNull_Test<sbyte?>();
+            await Perform_NullableNumber_RoundTripNull_Test<short?>();
+            await Perform_NullableNumber_RoundTripNull_Test<int?>();
+            await Perform_NullableNumber_RoundTripNull_Test<long?>();
+            await Perform_NullableNumber_RoundTripNull_Test<ushort?>();
+            await Perform_NullableNumber_RoundTripNull_Test<uint?>();
+            await Perform_NullableNumber_RoundTripNull_Test<ulong?>();
+            await Perform_NullableNumber_RoundTripNull_Test<float?>();
+            await Perform_NullableNumber_RoundTripNull_Test<decimal?>();
+        }
+
+        private async Task Perform_NullableNumber_RoundTripNull_Test<T>()
+        {
+            string nullAsJson = "null";
+            string nullAsQuotedJson = $@"""{nullAsJson}""";
+
+            Assert.Null(await Serializer.DeserializeWrapper<T>(nullAsJson, s_optionReadAndWriteFromStr));
+            Assert.Equal(nullAsJson, await Serializer.SerializeWrapper(default(T)));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<T>(nullAsQuotedJson, s_optionReadAndWriteFromStr));
+        }
+
+        [Fact]
+        public async Task Disallow_ArbritaryStrings_On_AllowFloatingPointConstants()
+        {
+            string json = @"""12345""";
+
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<byte>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<sbyte>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<short>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<int>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<long>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ushort>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<uint>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ulong>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<float>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<double>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<decimal>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<byte?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<sbyte?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<short?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<int?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<long?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ushort?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<uint?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ulong?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<float?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<double?>(json, s_optionsAllowFloatConstants));
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<decimal?>(json, s_optionsAllowFloatConstants));
+        }
+
+        [Fact]
+        public async Task Attributes_OnMembers_Work()
+        {
+            // Bad JSON because Int should not be string.
+            string intIsString = @"{""Float"":""1234.5"",""Int"":""12345""}";
+
+            // Good JSON because Float can be string.
+            string floatIsString = @"{""Float"":""1234.5"",""Int"":12345}";
+
+            // Good JSON because Float can be number.
+            string floatIsNumber = @"{""Float"":1234.5,""Int"":12345}";
+
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ClassWith_Attribute_OnNumber>(intIsString));
+
+            ClassWith_Attribute_OnNumber obj = await Serializer.DeserializeWrapper<ClassWith_Attribute_OnNumber>(floatIsString);
+            Assert.Equal(1234.5, obj.Float);
+            Assert.Equal(12345, obj.Int);
+
+            obj = await Serializer.DeserializeWrapper<ClassWith_Attribute_OnNumber>(floatIsNumber);
+            Assert.Equal(1234.5, obj.Float);
+            Assert.Equal(12345, obj.Int);
+
+            // Per options, float should be written as string.
+            JsonTestHelper.AssertJsonEqual(floatIsString, await Serializer.SerializeWrapper(obj));
+        }
+
+        public class ClassWith_Attribute_OnNumber
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public float Float { get; set; }
+
+            public int Int { get; set; }
+        }
+
+        [Fact]
+        public async Task Attribute_OnRootType_Works()
+        {
+            // Not allowed
+            string floatIsString = @"{""Float"":""1234"",""Int"":123}";
+
+            // Allowed
+            string floatIsNan = @"{""Float"":""NaN"",""Int"":123}";
+
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<Type_AllowFloatConstants>(floatIsString));
+
+            Type_AllowFloatConstants obj = await Serializer.DeserializeWrapper<Type_AllowFloatConstants>(floatIsNan);
+            Assert.Equal(float.NaN, obj.Float);
+            Assert.Equal(123, obj.Int);
+
+            JsonTestHelper.AssertJsonEqual(floatIsNan, await Serializer.SerializeWrapper(obj));
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.AllowNamedFloatingPointLiterals)]
+        public class Type_AllowFloatConstants
+        {
+            public float Float { get; set; }
+
+            public int Int { get; set; }
+        }
+
+        [Fact]
+        public async Task AttributeOnType_WinsOver_GlobalOption()
+        {
+            // Global options strict, type options loose
+            string json = @"{""Float"":""12345""}";
+            var obj1 = await Serializer.DeserializeWrapper<ClassWith_LooseAttribute>(json);
+
+            Assert.Equal(@"{""Float"":""12345""}", await Serializer.SerializeWrapper(obj1));
+
+            // Global options loose, type options strict
+            json = @"{""Float"":""12345""}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ClassWith_StrictAttribute>(json, s_optionReadAndWriteFromStr));
+
+            var obj2 = new ClassWith_StrictAttribute() { Float = 12345 };
+            Assert.Equal(@"{""Float"":12345}", await Serializer.SerializeWrapper(obj2, s_optionReadAndWriteFromStr));
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.Strict)]
+        public class ClassWith_StrictAttribute
+        {
+            public float Float { get; set; }
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+        public class ClassWith_LooseAttribute
+        {
+            public float Float { get; set; }
+        }
+
+        [Fact]
+        public async Task AttributeOnMember_WinsOver_AttributeOnType()
+        {
+            string json = @"{""Double"":""NaN""}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ClassWith_Attribute_On_TypeAndMember>(json));
+
+            var obj = new ClassWith_Attribute_On_TypeAndMember { Double = float.NaN };
+            await Assert.ThrowsAsync<ArgumentException>(async () => await Serializer.SerializeWrapper(obj));
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.AllowNamedFloatingPointLiterals)]
+        public class ClassWith_Attribute_On_TypeAndMember
+        {
+            [JsonNumberHandling(JsonNumberHandling.Strict)]
+            public double Double { get; set; }
+        }
+
+        [Fact]
+        public async Task Attribute_OnNestedType_Works()
+        {
+            string jsonWithShortProperty = @"{""Short"":""1""}";
+            ClassWith_ReadAsStringAttribute obj = await Serializer.DeserializeWrapper<ClassWith_ReadAsStringAttribute>(jsonWithShortProperty);
+            Assert.Equal(1, obj.Short);
+
+            string jsonWithMyObjectProperty = @"{""MyObject"":{""Float"":""1""}}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ClassWith_ReadAsStringAttribute>(jsonWithMyObjectProperty));
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
+        public class ClassWith_ReadAsStringAttribute
+        {
+            public short Short { get; set; }
+
+            public ClassWith_StrictAttribute MyObject { get; set; }
+        }
+
+        [Fact]
+        public async Task MemberAttributeAppliesToCollection_SimpleElements()
+        {
+            await RunTest<int[]>();
+            await RunTest<ConcurrentQueue<int>>();
+            await RunTest<GenericICollectionWrapper<int>>();
+            await RunTest<IEnumerable<int>>();
+            await RunTest<Collection<int>>();
+            await RunTest<ImmutableList<int>>();
+            await RunTest<HashSet<int>>();
+            await RunTest<List<int>>();
+            await RunTest<IList<int>>();
+            await RunTest<IList>();
+            await RunTest<Queue<int>>();
+
+            async Task RunTest<T>()
+            {
+                string json = @"{""MyList"":[""1"",""2""]}";
+                ClassWithSimpleCollectionProperty<T> obj = await Serializer.DeserializeWrapper<ClassWithSimpleCollectionProperty<T>>(json);
+                Assert.Equal(json, await Serializer.SerializeWrapper(obj));
+            }
+        }
+
+        public class ClassWithSimpleCollectionProperty<T>
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public T MyList { get; set; }
+        }
+
+        [Fact]
+        public async Task NestedCollectionElementTypeHandling_Overrides_GlobalOption()
+        {
+            // Strict policy on the collection element type overrides read-as-string on the collection property
+            string json = @"{""MyList"":[{""Float"":""1""}]}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ClassWithComplexListProperty>(json, s_optionReadAndWriteFromStr));
+
+            // Strict policy on the collection element type overrides write-as-string on the collection property
+            var obj = new ClassWithComplexListProperty
+            {
+                MyList = new List<ClassWith_StrictAttribute> { new ClassWith_StrictAttribute { Float = 1 } }
+            };
+            Assert.Equal(@"{""MyList"":[{""Float"":1}]}", await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr));
+        }
+
+        public class ClassWithComplexListProperty
+        {
+            public List<ClassWith_StrictAttribute> MyList { get; set; }
+        }
+
+        [Fact]
+        public async Task NumberHandlingAttribute_NotAllowedOn_CollectionOfNonNumbers()
+        {
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.DeserializeWrapper<ClassWith_AttributeOnComplexListProperty>(""));
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.SerializeWrapper(new ClassWith_AttributeOnComplexListProperty()));
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.DeserializeWrapper<ClassWith_AttributeOnComplexDictionaryProperty>(""));
+            await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.SerializeWrapper(new ClassWith_AttributeOnComplexDictionaryProperty()));
+        }
+
+        public class ClassWith_AttributeOnComplexListProperty
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public List<ClassWith_StrictAttribute> MyList { get; set; }
+        }
+
+        public class ClassWith_AttributeOnComplexDictionaryProperty
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
+            public Dictionary<string, ClassWith_StrictAttribute> MyDictionary { get; set; }
+        }
+
+        [Fact]
+        public async Task MemberAttributeAppliesToDictionary_SimpleElements()
+        {
+            string json = @"{""First"":""1"",""Second"":""2""}";
+            ClassWithSimpleDictionaryProperty obj = await Serializer.DeserializeWrapper<ClassWithSimpleDictionaryProperty>(json);
+        }
+
+        public class ClassWithSimpleDictionaryProperty
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public Dictionary<string, int> MyDictionary { get; set; }
+        }
+
+        [Fact]
+        public async Task NestedDictionaryElementTypeHandling_Overrides_GlobalOption()
+        {
+            // Strict policy on the dictionary element type overrides read-as-string on the collection property.
+            string json = @"{""MyDictionary"":{""Key"":{""Float"":""1""}}}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<ClassWithComplexDictionaryProperty>(json, s_optionReadFromStr));
+
+            // Strict policy on the collection element type overrides write-as-string on the collection property
+            var obj = new ClassWithComplexDictionaryProperty
+            {
+                MyDictionary = new Dictionary<string, ClassWith_StrictAttribute> { ["Key"] = new ClassWith_StrictAttribute { Float = 1 } }
+            };
+            Assert.Equal(@"{""MyDictionary"":{""Key"":{""Float"":1}}}", await Serializer.SerializeWrapper(obj, s_optionReadFromStr));
+        }
+
+        public class ClassWithComplexDictionaryProperty
+        {
+            public Dictionary<string, ClassWith_StrictAttribute> MyDictionary { get; set; }
+        }
+
+        [Fact]
+        public async Task TypeAttributeAppliesTo_CustomCollectionElements()
+        {
+            string json = @"[""1""]";
+            MyCustomList obj = await Serializer.DeserializeWrapper<MyCustomList>(json);
+            Assert.Equal(json, await Serializer.SerializeWrapper(obj));
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+        public class MyCustomList : List<int> { }
+
+        [Fact]
+        public async Task TypeAttributeAppliesTo_CustomCollectionElements_HonoredWhenProperty()
+        {
+            string json = @"{""List"":[""1""]}";
+            ClassWithCustomList obj = await Serializer.DeserializeWrapper<ClassWithCustomList>(json);
+            Assert.Equal(json, await Serializer.SerializeWrapper(obj));
+        }
+
+        public class ClassWithCustomList
+        {
+            public MyCustomList List { get; set; }
+        }
+
+        [Fact]
+        public async Task TypeAttributeAppliesTo_CustomDictionaryElements()
+        {
+            string json = @"{""Key"":""1""}";
+            MyCustomDictionary obj = await Serializer.DeserializeWrapper<MyCustomDictionary>(json);
+            Assert.Equal(json, await Serializer.SerializeWrapper(obj));
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+        public class MyCustomDictionary : Dictionary<string, int> { }
+
+        [Fact]
+        public async Task TypeAttributeAppliesTo_CustomDictionaryElements_HonoredWhenProperty()
+        {
+            string json = @"{""Dictionary"":{""Key"":""1""}}";
+            ClassWithCustomDictionary obj = await Serializer.DeserializeWrapper<ClassWithCustomDictionary>(json);
+            Assert.Equal(json, await Serializer.SerializeWrapper(obj));
+        }
+
+        public class ClassWithCustomDictionary
+        {
+            public MyCustomDictionary Dictionary { get; set; }
+        }
+
+        [Fact]
+        public async Task Attribute_OnType_NotRecursive()
+        {
+            // Recursive behavior, where number handling setting on a property is applied to subsequent
+            // properties in its type closure, would allow a string number. This is not supported.
+            string json = @"{""NestedClass"":{""MyInt"":""1""}}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<AttributeAppliedToFirstLevelProp>(json));
+
+            var obj = new AttributeAppliedToFirstLevelProp
+            {
+                NestedClass = new NonNumberType { MyInt = 1 }
+            };
+            Assert.Equal(@"{""NestedClass"":{""MyInt"":1}}", await Serializer.SerializeWrapper(obj));
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+        public class AttributeAppliedToFirstLevelProp
+        {
+            public NonNumberType NestedClass { get; set; }
+        }
+
+        public class NonNumberType
+        {
+            public int MyInt { get; set; }
+        }
+
+        [Fact]
+        public async Task HandlingOnMemberOverridesHandlingOnType_Enumerable()
+        {
+            string json = @"{""List"":[""1""]}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<MyCustomListWrapper>(json));
+
+            var obj = new MyCustomListWrapper
+            {
+                List = new MyCustomList { 1 }
+            };
+            Assert.Equal(@"{""List"":[1]}", await Serializer.SerializeWrapper(obj));
+        }
+
+        public class MyCustomListWrapper
+        {
+            [JsonNumberHandling(JsonNumberHandling.Strict)]
+            public MyCustomList List { get; set; }
+        }
+
+        [Fact]
+        public async Task HandlingOnMemberOverridesHandlingOnType_Dictionary()
+        {
+            string json = @"{""Dictionary"":{""Key"":""1""}}";
+            await Assert.ThrowsAsync<JsonException>(async () => await Serializer.DeserializeWrapper<MyCustomDictionaryWrapper>(json));
+
+            var obj1 = new MyCustomDictionaryWrapper
+            {
+                Dictionary = new MyCustomDictionary { ["Key"] = 1 }
+            };
+            Assert.Equal(@"{""Dictionary"":{""Key"":1}}", await Serializer.SerializeWrapper(obj1));
+        }
+
+        public class MyCustomDictionaryWrapper
+        {
+            [JsonNumberHandling(JsonNumberHandling.Strict)]
+            public MyCustomDictionary Dictionary { get; set; }
+        }
+
+        [Fact]
+        public async Task Attribute_Allowed_On_NonNumber_NonCollection_Property()
+        {
+            const string Json = @"{""MyProp"":{""MyInt"":1}}";
+
+            ClassWith_NumberHandlingOn_ObjectProperty obj = await Serializer.DeserializeWrapper<ClassWith_NumberHandlingOn_ObjectProperty>(Json);
+            Assert.Equal(1, obj.MyProp.MyInt);
+
+            string json = await Serializer.SerializeWrapper(obj);
+            Assert.Equal(Json, json);
+        }
+
+        public class ClassWith_NumberHandlingOn_ObjectProperty
+        {
+            [JsonNumberHandling(JsonNumberHandling.Strict)]
+            public NonNumberType MyProp { get; set; }
+        }
+
+        [Fact]
+        public async Task Attribute_Allowed_On_Property_WithCustomConverter()
+        {
+            string json = @"{""Prop"":1}";
+
+            // Converter returns 25 regardless of input.
+            var obj = await Serializer.DeserializeWrapper<ClassWith_NumberHandlingOn_Property_WithCustomConverter>(json);
+            Assert.Equal(25, obj.Prop);
+
+            // Converter throws this exception regardless of input.
+            NotImplementedException ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(obj));
+            Assert.Equal("Converter was called", ex.Message);
+        }
+
+        public class ClassWith_NumberHandlingOn_Property_WithCustomConverter
+        {
+            [JsonNumberHandling(JsonNumberHandling.Strict)]
+            [JsonConverter(typeof(ConverterForInt32))]
+            public int Prop { get; set; }
+        }
+
+        [Fact]
+        public async Task Attribute_Allowed_On_Type_WithCustomConverter()
+        {
+            string json = @"{}";
+            NotImplementedException ex;
+
+            // Assert regular Read/Write methods on custom converter are called.
+            ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.DeserializeWrapper<ClassWith_NumberHandlingOn_Type_WithCustomConverter>(json));
+            Assert.Equal("Converter was called", ex.Message);
+
+            ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(new ClassWith_NumberHandlingOn_Type_WithCustomConverter()));
+            Assert.Equal("Converter was called", ex.Message);
+        }
+
+        [JsonNumberHandling(JsonNumberHandling.Strict)]
+        [JsonConverter(typeof(ConverterForMyType))]
+        public class ClassWith_NumberHandlingOn_Type_WithCustomConverter
+        {
+        }
+
+        public class ConverterForMyType : JsonConverter<ClassWith_NumberHandlingOn_Type_WithCustomConverter>
+        {
+            public override ClassWith_NumberHandlingOn_Type_WithCustomConverter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            {
+                throw new NotImplementedException("Converter was called");
+            }
+
+            public override void Write(Utf8JsonWriter writer, ClassWith_NumberHandlingOn_Type_WithCustomConverter value, JsonSerializerOptions options)
+            {
+                throw new NotImplementedException("Converter was called");
+            }
+        }
+
+        [Fact]
+        public async Task CustomConverterOverridesBuiltInLogic()
+        {
+            var options = new JsonSerializerOptions(s_optionReadAndWriteFromStr)
+            {
+                Converters = { new ConverterForInt32(), new ConverterForFloat() }
+            };
+
+            string json = @"""32""";
+
+            // Converter returns 25 regardless of input.
+            Assert.Equal(25, await Serializer.DeserializeWrapper<int>(json, options));
+
+            // Converter throws this exception regardless of input.
+            NotImplementedException ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(4, options));
+            Assert.Equal("Converter was called", ex.Message);
+
+            json = @"""NaN""";
+
+            // Converter returns 25 if NaN.
+            Assert.Equal(25, await Serializer.DeserializeWrapper<float?>(json, options));
+
+            // Converter writes 25 if NaN.
+            Assert.Equal("25", await Serializer.SerializeWrapper((float?)float.NaN, options));
+        }
+
+        public class ConverterForFloat : JsonConverter<float?>
+        {
+            public override float? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            {
+                if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN")
+                {
+                    return 25;
+                }
+
+                throw new NotSupportedException();
+            }
+
+            public override void Write(Utf8JsonWriter writer, float? value, JsonSerializerOptions options)
+            {
+                if (float.IsNaN(value.Value))
+                {
+                    writer.WriteNumberValue(25);
+                    return;
+                }
+
+                throw new NotSupportedException();
+            }
+        }
+
+        [Fact]
+        public static void JsonNumberHandling_ArgOutOfRangeFail()
+        {
+            // Global options
+            ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(
+                () => new JsonSerializerOptions { NumberHandling = (JsonNumberHandling)(-1) });
+            Assert.Contains("value", ex.ToString());
+            Assert.Throws<ArgumentOutOfRangeException>(
+                () => new JsonSerializerOptions { NumberHandling = (JsonNumberHandling)(8) });
+
+            ex = Assert.Throws<ArgumentOutOfRangeException>(
+                () => new JsonNumberHandlingAttribute((JsonNumberHandling)(-1)));
+            Assert.Contains("handling", ex.ToString());
+            Assert.Throws<ArgumentOutOfRangeException>(
+                () => new JsonNumberHandlingAttribute((JsonNumberHandling)(8)));
+        }
+
+        [Fact]
+        public async Task InternalCollectionConverter_CustomNumberConverter_GlobalOption()
+        {
+            NotImplementedException ex;
+
+            var list = new List<int> { 1 };
+            var options = new JsonSerializerOptions(s_optionReadAndWriteFromStr)
+            {
+                Converters = { new ConverterForInt32() }
+            };
+
+            // Assert converter methods are called and not Read/WriteWithNumberHandling (which would throw InvalidOperationException).
+            // Converter returns 25 regardless of input.
+            Assert.Equal(25, (await Serializer.DeserializeWrapper<List<int>>(@"[""1""]", options))[0]);
+            // Converter throws this exception regardless of input.
+            ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(list, options));
+            Assert.Equal("Converter was called", ex.Message);
+
+            var list2 = new List<int?> { 1 };
+            Assert.Equal(25, (await Serializer.DeserializeWrapper<List<int?>>(@"[""1""]", options))[0]);
+            ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(list2, options));
+            Assert.Equal("Converter was called", ex.Message);
+
+            // Okay to set number handling for number collection property when number is handled with custom converter;
+            // converter Read/Write methods called.
+            ClassWithListPropAndAttribute obj1 = await Serializer.DeserializeWrapper<ClassWithListPropAndAttribute>(@"{""Prop"":[""1""]}", options);
+            Assert.Equal(25, obj1.Prop[0]);
+            ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(obj1, options));
+            Assert.Equal("Converter was called", ex.Message);
+
+            ClassWithDictPropAndAttribute obj2 = await Serializer.DeserializeWrapper<ClassWithDictPropAndAttribute>(@"{""Prop"":{""1"":""1""}}", options);
+            Assert.Equal(25, obj2.Prop[1]);
+            ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(obj2, options));
+            Assert.Equal("Converter was called", ex.Message);
+        }
+
+        public class ClassWithListPropAndAttribute
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public List<int> Prop { get; set; }
+        }
+
+        public class ClassWithDictPropAndAttribute
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            public Dictionary<int, int?> Prop { get; set; }
+        }
+
+        [Fact]
+        public async Task InternalCollectionConverter_CustomNumberConverter_OnProperty()
+        {
+            // Invalid to set number handling for number collection property when number is handled with custom converter.
+            var ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.DeserializeWrapper<ClassWithListPropAndAttribute_ConverterOnProp>(""));
+            Assert.Contains(nameof(ClassWithListPropAndAttribute_ConverterOnProp), ex.ToString());
+            Assert.Contains("IntProp", ex.ToString());
+
+            ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.SerializeWrapper(new ClassWithListPropAndAttribute_ConverterOnProp()));
+            Assert.Contains(nameof(ClassWithListPropAndAttribute_ConverterOnProp), ex.ToString());
+            Assert.Contains("IntProp", ex.ToString());
+
+#if !BUILDING_SOURCE_GENERATOR_TESTS
+            // Source-gen isn't currently validating that the converter on the test prop
+            // is invalid so JsonException is being thrown instead due to invalid JSON.
+            // [ActiveIssue("https://github.com/dotnet/runtime/issues/73714"]
+            ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.DeserializeWrapper<ClassWithDictPropAndAttribute_ConverterOnProp>(""));
+            Assert.Contains(nameof(ClassWithDictPropAndAttribute_ConverterOnProp), ex.ToString());
+            Assert.Contains("IntProp", ex.ToString());
+
+            ex = await Assert.ThrowsAsync<InvalidOperationException>(async () => await Serializer.SerializeWrapper(new ClassWithDictPropAndAttribute_ConverterOnProp()));
+            Assert.Contains(nameof(ClassWithDictPropAndAttribute_ConverterOnProp), ex.ToString());
+            Assert.Contains("IntProp", ex.ToString());
+#endif
+        }
+
+        public class ClassWithListPropAndAttribute_ConverterOnProp
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            [JsonConverter(typeof(ListOfIntConverter))]
+            public List<int> IntProp { get; set; }
+        }
+
+        public class ClassWithDictPropAndAttribute_ConverterOnProp
+        {
+            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
+            [JsonConverter(typeof(ClassWithDictPropAndAttribute_ConverterOnProp))]
+            public Dictionary<int, int?> IntProp { get; set; }
+        }
+
+        public class ListOfIntConverter : JsonConverter<List<int>>
+        {
+            public override List<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException();
+            public override void Write(Utf8JsonWriter writer, List<int> value, JsonSerializerOptions options) => throw new NotImplementedException();
+        }
+
+        [Fact]
+        public async Task InternalCollectionConverter_CustomNullableNumberConverter()
+        {
+            NotImplementedException ex;
+
+            var dict = new Dictionary<int, int?> { [1] = 1 };
+            var options = new JsonSerializerOptions(s_optionReadAndWriteFromStr)
+            {
+                Converters = { new ConverterForNullableInt32() }
+            };
+
+            // Assert converter methods are called and not Read/WriteWithNumberHandling (which would throw InvalidOperationException).
+            // Converter returns 25 regardless of input.
+            Assert.Equal(25, (await Serializer.DeserializeWrapper<Dictionary<int, int?>>(@"{""1"":""1""}", options))[1]);
+            ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(dict, options));
+            Assert.Equal("Converter was called", ex.Message);
+
+            var obj = await Serializer.DeserializeWrapper<ClassWithDictPropAndAttribute>(@"{""Prop"":{""1"":""1""}}", options);
+            Assert.Equal(25, obj.Prop[1]);
+            ex = await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(obj, options));
+            await Assert.ThrowsAsync<NotImplementedException>(async () => await Serializer.SerializeWrapper(dict, options));
+            Assert.Equal("Converter was called", ex.Message);
+        }
+
+        public class ConverterForNullableInt32 : JsonConverter<int?>
+        {
+            public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            {
+                return 25;
+            }
+
+            public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options)
+            {
+                throw new NotImplementedException("Converter was called");
+            }
+        }
+
+        /// <summary>
+        /// Example of a custom converter that uses the options to determine behavior.
+        /// </summary>
+        [Fact]
+        public async Task AdaptableCustomConverter()
+        {
+            // Baseline without custom converter
+            PlainClassWithList obj = new() { Prop = new List<int>() { 1 } };
+            string json = await Serializer.SerializeWrapper(obj, s_optionReadAndWriteFromStr);
+            Assert.Equal("{\"Prop\":[\"1\"]}", json);
+
+            obj = await Serializer.DeserializeWrapper<PlainClassWithList>(json, s_optionReadAndWriteFromStr);
+            Assert.Equal(1, obj.Prop[0]);
+
+            // First with numbers
+            JsonSerializerOptions options = new()
+            {
+                Converters = { new AdaptableInt32Converter() }
+            };
+
+            obj = new PlainClassWithList() { Prop = new List<int>() { 1 } };
+            json = await Serializer.SerializeWrapper(obj, options);
+            Assert.Equal("{\"Prop\":[101]}", json);
+
+            obj = await Serializer.DeserializeWrapper<PlainClassWithList>(json, options);
+            Assert.Equal(1, obj.Prop[0]);
+
+            // Then with strings
+            options = new JsonSerializerOptions()
+            {
+                NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString,
+                Converters = { new AdaptableInt32Converter() }
+            };
+
+            obj = new PlainClassWithList() { Prop = new List<int>() { 1 } };
+            json = await Serializer.SerializeWrapper(obj, options);
+            Assert.Equal("{\"Prop\":[\"101\"]}", json);
+
+            obj = await Serializer.DeserializeWrapper<PlainClassWithList>(json, options);
+            Assert.Equal(1, obj.Prop[0]);
+        }
+
+        public class PlainClassWithList
+        {
+            public List<int> Prop { get; set; }
+        }
+
+        public class AdaptableInt32Converter : JsonConverter<int>
+        {
+            public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            {
+                if ((JsonNumberHandling.AllowReadingFromString & options.NumberHandling) != 0)
+                {
+                    // Assume it's a string; don't use TryParse().
+                    return int.Parse(reader.GetString(), CultureInfo.InvariantCulture) - 100;
+                }
+                else
+                {
+                    return reader.GetInt32() - 100;
+                }
+            }
+
+            public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
+            {
+                if ((JsonNumberHandling.WriteAsString & options.NumberHandling) != 0)
+                {
+                    writer.WriteStringValue((value + 100).ToString(CultureInfo.InvariantCulture));
+                }
+                else
+                {
+                    writer.WriteNumberValue(value + 100);
+                }
+            }
+        }
+    }
+}
index 3a8a3b6..9377445 100644 (file)
@@ -1894,6 +1894,15 @@ namespace System.Text.Json.Serialization.Tests
         {
             throw new NotImplementedException("Converter was called");
         }
+
+        // In source-gen, internal converters are not used as fallbacks when custom converters don't provide an implementation.
+#if BUILDING_SOURCE_GENERATOR_TESTS
+        public override int ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+            => int.Parse(reader.GetString());
+
+        public override void WriteAsPropertyName(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
+            => writer.WritePropertyName(value.ToString());
+#endif
     }
 
     public class SimpleSnakeCasePolicy : JsonNamingPolicy
index 863d86b..ed29ed8 100644 (file)
@@ -57,6 +57,7 @@ namespace System.Text.Json.SourceGeneration.Tests
         public JsonTypeInfo<TypeWithValidationAttributes> TypeWithValidationAttributes { get; }
         public JsonTypeInfo<TypeWithDerivedAttribute> TypeWithDerivedAttribute { get; }
         public JsonTypeInfo<PolymorphicClass> PolymorphicClass { get; }
+        public JsonTypeInfo<PocoWithNumberHandlingAttr> PocoWithNumberHandlingAttr { get; }
     }
 
     internal partial class JsonContext : JsonSerializerContext
index 8ebf4c2..4ff998b 100644 (file)
@@ -53,6 +53,7 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(TypeWithValidationAttributes))]
     [JsonSerializable(typeof(TypeWithDerivedAttribute))]
     [JsonSerializable(typeof(PolymorphicClass))]
+    [JsonSerializable(typeof(PocoWithNumberHandlingAttr))]
     internal partial class MetadataAndSerializationContext : JsonSerializerContext, ITestContext
     {
         public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Default;
index 5100371..33683df 100644 (file)
@@ -52,6 +52,7 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(TypeWithValidationAttributes), GenerationMode = JsonSourceGenerationMode.Metadata)]
     [JsonSerializable(typeof(TypeWithDerivedAttribute), GenerationMode = JsonSourceGenerationMode.Metadata)]
     [JsonSerializable(typeof(PolymorphicClass), GenerationMode = JsonSourceGenerationMode.Metadata)]
+    [JsonSerializable(typeof(PocoWithNumberHandlingAttr), GenerationMode = JsonSourceGenerationMode.Metadata)]
     internal partial class MetadataWithPerTypeAttributeContext : JsonSerializerContext, ITestContext
     {
         public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Metadata;
@@ -154,6 +155,7 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(TypeWithValidationAttributes))]
     [JsonSerializable(typeof(TypeWithDerivedAttribute))]
     [JsonSerializable(typeof(PolymorphicClass))]
+    [JsonSerializable(typeof(PocoWithNumberHandlingAttr))]
     internal partial class MetadataContext : JsonSerializerContext, ITestContext
     {
         public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Metadata;
index bd5a525..72b9a9b 100644 (file)
@@ -53,6 +53,7 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(TypeWithValidationAttributes), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(TypeWithDerivedAttribute), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(PolymorphicClass), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
+    [JsonSerializable(typeof(PocoWithNumberHandlingAttr), GenerationMode = JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization)]
     internal partial class MixedModeContext : JsonSerializerContext, ITestContext
     {
         public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Metadata | JsonSourceGenerationMode.Serialization;
index d0cef83..420df16 100644 (file)
@@ -1097,5 +1097,18 @@ namespace System.Text.Json.SourceGeneration.Tests
                 Assert.True(derivedResult.Boolean);
             }
         }
+
+        [Fact]
+        public void NumberHandlingHonoredOnPoco()
+        {
+            if (DefaultContext.JsonSourceGenerationMode == JsonSourceGenerationMode.Serialization)
+            {
+                Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(new PocoWithNumberHandlingAttr(), DefaultContext.PocoWithNumberHandlingAttr));
+            }
+            else
+            {
+                JsonTestHelper.AssertJsonEqual(@"{""Id"":""0""}", JsonSerializer.Serialize(new PocoWithNumberHandlingAttr(), DefaultContext.PocoWithNumberHandlingAttr));
+            }
+        }
     }
 }
diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/NumberHandlingTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.SourceGeneration.Tests/Serialization/NumberHandlingTests.cs
new file mode 100644 (file)
index 0000000..36f68c8
--- /dev/null
@@ -0,0 +1,780 @@
+// 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.Collections.Immutable;
+using System.Collections.ObjectModel;
+using System.Text.Json.Serialization;
+using System.Text.Json.Serialization.Tests;
+
+namespace System.Text.Json.SourceGeneration.Tests
+{
+    public sealed partial class NumberHandlingTests_Metadata : NumberHandlingTests
+    {
+        public NumberHandlingTests_Metadata()
+            : base(new StringSerializerWrapper(NumberHandlingTestsContext_Metadata.Default))
+        {
+        }
+
+        [JsonSourceGenerationOptions(GenerationMode = JsonSourceGenerationMode.Metadata)]
+        [JsonSerializable(typeof(Class_With_BoxedNumbers))]
+        [JsonSerializable(typeof(Class_With_ListsOfBoxedNumbers))]
+        [JsonSerializable(typeof(Class_With_BoxedNonNumbers))]
+        [JsonSerializable(typeof(Class_With_ListsOfBoxedNonNumbers))]
+        [JsonSerializable(typeof(ClassWithNumbers))]
+        [JsonSerializable(typeof(ClassWith_StrictAttribute))]
+        [JsonSerializable(typeof(ClassWith_ReadAsStringAttribute))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<int[]>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<ConcurrentQueue<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<GenericICollectionWrapper<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<IEnumerable<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<Collection<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<ImmutableList<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<HashSet<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<List<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<IList<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<IList>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<Queue<int>>))]
+        [JsonSerializable(typeof(ClassWithComplexListProperty))]
+        [JsonSerializable(typeof(ClassWith_AttributeOnComplexListProperty))]
+        [JsonSerializable(typeof(ClassWith_AttributeOnComplexDictionaryProperty))]
+        [JsonSerializable(typeof(ClassWithSimpleDictionaryProperty))]
+        [JsonSerializable(typeof(ClassWithComplexDictionaryProperty))]
+        [JsonSerializable(typeof(MyCustomList))]
+        [JsonSerializable(typeof(ClassWithCustomList))]
+        [JsonSerializable(typeof(MyCustomDictionary))]
+        [JsonSerializable(typeof(ClassWithCustomDictionary))]
+        [JsonSerializable(typeof(AttributeAppliedToFirstLevelProp))]
+        [JsonSerializable(typeof(NonNumberType))]
+        [JsonSerializable(typeof(MyCustomListWrapper))]
+        [JsonSerializable(typeof(MyCustomDictionaryWrapper))]
+        [JsonSerializable(typeof(ClassWith_NumberHandlingOn_ObjectProperty))]
+        [JsonSerializable(typeof(ClassWith_NumberHandlingOn_Property_WithCustomConverter))]
+        [JsonSerializable(typeof(ClassWith_NumberHandlingOn_Type_WithCustomConverter))]
+        [JsonSerializable(typeof(Class_With_NullableUInt64_And_Float))]
+        [JsonSerializable(typeof(MyClassWithNumbers))]
+        [JsonSerializable(typeof(MyClassWithNumbers_PropsHasAttribute))]
+        [JsonSerializable(typeof(ClassWith_Attribute_OnNumber))]
+        [JsonSerializable(typeof(Type_AllowFloatConstants))]
+        [JsonSerializable(typeof(ClassWith_LooseAttribute))]
+        [JsonSerializable(typeof(ClassWith_Attribute_On_TypeAndMember))]
+        [JsonSerializable(typeof(ClassWithListPropAndAttribute))]
+        [JsonSerializable(typeof(ClassWithDictPropAndAttribute))]
+        [JsonSerializable(typeof(ClassWithListPropAndAttribute_ConverterOnProp))]
+        [JsonSerializable(typeof(ClassWithDictPropAndAttribute_ConverterOnProp))]
+        [JsonSerializable(typeof(PlainClassWithList))]
+        [JsonSerializable(typeof(StructWithNumbers))]
+        [JsonSerializable(typeof(DateTime))]
+        [JsonSerializable(typeof(Guid?))]
+        [JsonSerializable(typeof(byte))]
+        [JsonSerializable(typeof(sbyte))]
+        [JsonSerializable(typeof(short))]
+        [JsonSerializable(typeof(int))]
+        [JsonSerializable(typeof(long))]
+        [JsonSerializable(typeof(ushort))]
+        [JsonSerializable(typeof(uint))]
+        [JsonSerializable(typeof(ulong))]
+        [JsonSerializable(typeof(float))]
+        [JsonSerializable(typeof(double))]
+        [JsonSerializable(typeof(decimal))]
+        [JsonSerializable(typeof(byte?))]
+        [JsonSerializable(typeof(sbyte?))]
+        [JsonSerializable(typeof(short?))]
+        [JsonSerializable(typeof(int?))]
+        [JsonSerializable(typeof(long?))]
+        [JsonSerializable(typeof(ushort?))]
+        [JsonSerializable(typeof(uint?))]
+        [JsonSerializable(typeof(ulong?))]
+        [JsonSerializable(typeof(float?))]
+        [JsonSerializable(typeof(double?))]
+        [JsonSerializable(typeof(decimal?))]
+        [JsonSerializable(typeof(IEnumerable<string>))]
+        [JsonSerializable(typeof(List<float>))]
+        [JsonSerializable(typeof(List<double>))]
+        [JsonSerializable(typeof(List<decimal>))]
+        [JsonSerializable(typeof(KeyValuePair<ulong?, List<float>>))]
+        [JsonSerializable(typeof(Dictionary<int, float>))]
+        [JsonSerializable(typeof(Dictionary<string, ulong>))]
+        [JsonSerializable(typeof(JsonElement))]
+        [JsonSerializable(typeof(Queue))]
+        [JsonSerializable(typeof(WrapperForIList))]
+        [JsonSerializable(typeof(byte[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<byte>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<byte>))]
+        [JsonSerializable(typeof(IEnumerable<byte>))]
+        [JsonSerializable(typeof(Collection<byte>))]
+        [JsonSerializable(typeof(HashSet<byte>))]
+        [JsonSerializable(typeof(List<byte>))]
+        [JsonSerializable(typeof(Queue<byte>))]
+        [JsonSerializable(typeof(ImmutableList<byte>))]
+        [JsonSerializable(typeof(sbyte[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<sbyte>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<sbyte>))]
+        [JsonSerializable(typeof(IEnumerable<sbyte>))]
+        [JsonSerializable(typeof(Collection<sbyte>))]
+        [JsonSerializable(typeof(HashSet<sbyte>))]
+        [JsonSerializable(typeof(List<sbyte>))]
+        [JsonSerializable(typeof(Queue<sbyte>))]
+        [JsonSerializable(typeof(ImmutableList<sbyte>))]
+        [JsonSerializable(typeof(short[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<short>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<short>))]
+        [JsonSerializable(typeof(IEnumerable<short>))]
+        [JsonSerializable(typeof(Collection<short>))]
+        [JsonSerializable(typeof(HashSet<short>))]
+        [JsonSerializable(typeof(List<short>))]
+        [JsonSerializable(typeof(Queue<short>))]
+        [JsonSerializable(typeof(ImmutableList<short>))]
+        [JsonSerializable(typeof(int[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<int>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<int>))]
+        [JsonSerializable(typeof(IEnumerable<int>))]
+        [JsonSerializable(typeof(Collection<int>))]
+        [JsonSerializable(typeof(HashSet<int>))]
+        [JsonSerializable(typeof(List<int>))]
+        [JsonSerializable(typeof(Queue<int>))]
+        [JsonSerializable(typeof(ImmutableList<int>))]
+        [JsonSerializable(typeof(long[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<long>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<long>))]
+        [JsonSerializable(typeof(IEnumerable<long>))]
+        [JsonSerializable(typeof(Collection<long>))]
+        [JsonSerializable(typeof(HashSet<long>))]
+        [JsonSerializable(typeof(List<long>))]
+        [JsonSerializable(typeof(Queue<long>))]
+        [JsonSerializable(typeof(ImmutableList<long>))]
+        [JsonSerializable(typeof(ushort[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ushort>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ushort>))]
+        [JsonSerializable(typeof(IEnumerable<ushort>))]
+        [JsonSerializable(typeof(Collection<ushort>))]
+        [JsonSerializable(typeof(HashSet<ushort>))]
+        [JsonSerializable(typeof(List<ushort>))]
+        [JsonSerializable(typeof(Queue<ushort>))]
+        [JsonSerializable(typeof(ImmutableList<ushort>))]
+        [JsonSerializable(typeof(uint[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<uint>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<uint>))]
+        [JsonSerializable(typeof(IEnumerable<uint>))]
+        [JsonSerializable(typeof(Collection<uint>))]
+        [JsonSerializable(typeof(HashSet<uint>))]
+        [JsonSerializable(typeof(List<uint>))]
+        [JsonSerializable(typeof(Queue<uint>))]
+        [JsonSerializable(typeof(ImmutableList<uint>))]
+        [JsonSerializable(typeof(short?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<short?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<short?>))]
+        [JsonSerializable(typeof(IEnumerable<short?>))]
+        [JsonSerializable(typeof(Collection<short?>))]
+        [JsonSerializable(typeof(HashSet<short?>))]
+        [JsonSerializable(typeof(List<short?>))]
+        [JsonSerializable(typeof(Queue<short?>))]
+        [JsonSerializable(typeof(ImmutableList<short?>))]
+        [JsonSerializable(typeof(int?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<int?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<int?>))]
+        [JsonSerializable(typeof(IEnumerable<int?>))]
+        [JsonSerializable(typeof(Collection<int?>))]
+        [JsonSerializable(typeof(HashSet<int?>))]
+        [JsonSerializable(typeof(List<int?>))]
+        [JsonSerializable(typeof(Queue<int?>))]
+        [JsonSerializable(typeof(ImmutableList<int?>))]
+        [JsonSerializable(typeof(long?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<long?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<long?>))]
+        [JsonSerializable(typeof(IEnumerable<long?>))]
+        [JsonSerializable(typeof(Collection<long?>))]
+        [JsonSerializable(typeof(HashSet<long?>))]
+        [JsonSerializable(typeof(List<long?>))]
+        [JsonSerializable(typeof(Queue<long?>))]
+        [JsonSerializable(typeof(ImmutableList<long?>))]
+        [JsonSerializable(typeof(ushort?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ushort?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ushort?>))]
+        [JsonSerializable(typeof(IEnumerable<ushort?>))]
+        [JsonSerializable(typeof(Collection<ushort?>))]
+        [JsonSerializable(typeof(HashSet<ushort?>))]
+        [JsonSerializable(typeof(List<ushort?>))]
+        [JsonSerializable(typeof(Queue<ushort?>))]
+        [JsonSerializable(typeof(ImmutableList<ushort?>))]
+        [JsonSerializable(typeof(uint?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<uint?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<uint?>))]
+        [JsonSerializable(typeof(IEnumerable<uint?>))]
+        [JsonSerializable(typeof(Collection<uint?>))]
+        [JsonSerializable(typeof(HashSet<uint?>))]
+        [JsonSerializable(typeof(List<uint?>))]
+        [JsonSerializable(typeof(Queue<uint?>))]
+        [JsonSerializable(typeof(ImmutableList<uint?>))]
+        [JsonSerializable(typeof(ulong[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ulong>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ulong>))]
+        [JsonSerializable(typeof(IEnumerable<ulong>))]
+        [JsonSerializable(typeof(Collection<ulong>))]
+        [JsonSerializable(typeof(HashSet<ulong>))]
+        [JsonSerializable(typeof(List<ulong>))]
+        [JsonSerializable(typeof(Queue<ulong>))]
+        [JsonSerializable(typeof(ImmutableList<ulong>))]
+        [JsonSerializable(typeof(float[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<float>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<float>))]
+        [JsonSerializable(typeof(IEnumerable<float>))]
+        [JsonSerializable(typeof(Collection<float>))]
+        [JsonSerializable(typeof(HashSet<float>))]
+        [JsonSerializable(typeof(List<float>))]
+        [JsonSerializable(typeof(Queue<float>))]
+        [JsonSerializable(typeof(ImmutableList<float>))]
+        [JsonSerializable(typeof(double[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<double>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<double>))]
+        [JsonSerializable(typeof(IEnumerable<double>))]
+        [JsonSerializable(typeof(Collection<double>))]
+        [JsonSerializable(typeof(HashSet<double>))]
+        [JsonSerializable(typeof(List<double>))]
+        [JsonSerializable(typeof(Queue<double>))]
+        [JsonSerializable(typeof(ImmutableList<double>))]
+        [JsonSerializable(typeof(decimal[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<decimal>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<decimal>))]
+        [JsonSerializable(typeof(IEnumerable<decimal>))]
+        [JsonSerializable(typeof(Collection<decimal>))]
+        [JsonSerializable(typeof(HashSet<decimal>))]
+        [JsonSerializable(typeof(List<decimal>))]
+        [JsonSerializable(typeof(Queue<decimal>))]
+        [JsonSerializable(typeof(ImmutableList<decimal>))]
+        [JsonSerializable(typeof(List<byte>))]
+        [JsonSerializable(typeof(List<sbyte>))]
+        [JsonSerializable(typeof(List<short>))]
+        [JsonSerializable(typeof(List<int>))]
+        [JsonSerializable(typeof(List<long>))]
+        [JsonSerializable(typeof(List<ushort>))]
+        [JsonSerializable(typeof(List<uint>))]
+        [JsonSerializable(typeof(List<ulong>))]
+        [JsonSerializable(typeof(List<float>))]
+        [JsonSerializable(typeof(List<double>))]
+        [JsonSerializable(typeof(List<decimal>))]
+        [JsonSerializable(typeof(List<byte?>))]
+        [JsonSerializable(typeof(List<sbyte?>))]
+        [JsonSerializable(typeof(List<short?>))]
+        [JsonSerializable(typeof(List<int?>))]
+        [JsonSerializable(typeof(List<long?>))]
+        [JsonSerializable(typeof(List<ushort?>))]
+        [JsonSerializable(typeof(List<uint?>))]
+        [JsonSerializable(typeof(List<ulong?>))]
+        [JsonSerializable(typeof(List<float?>))]
+        [JsonSerializable(typeof(List<double?>))]
+        [JsonSerializable(typeof(List<decimal?>))]
+        [JsonSerializable(typeof(Hashtable))]
+        [JsonSerializable(typeof(byte?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<byte?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<byte?>))]
+        [JsonSerializable(typeof(IEnumerable<byte?>))]
+        [JsonSerializable(typeof(Collection<byte?>))]
+        [JsonSerializable(typeof(HashSet<byte?>))]
+        [JsonSerializable(typeof(List<byte?>))]
+        [JsonSerializable(typeof(Queue<byte?>))]
+        [JsonSerializable(typeof(ImmutableList<byte?>))]
+        [JsonSerializable(typeof(sbyte?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<sbyte?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<sbyte?>))]
+        [JsonSerializable(typeof(IEnumerable<sbyte?>))]
+        [JsonSerializable(typeof(Collection<sbyte?>))]
+        [JsonSerializable(typeof(HashSet<sbyte?>))]
+        [JsonSerializable(typeof(List<sbyte?>))]
+        [JsonSerializable(typeof(Queue<sbyte?>))]
+        [JsonSerializable(typeof(ImmutableList<sbyte?>))]
+        [JsonSerializable(typeof(short?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<short?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<short?>))]
+        [JsonSerializable(typeof(IEnumerable<short?>))]
+        [JsonSerializable(typeof(Collection<short?>))]
+        [JsonSerializable(typeof(HashSet<short?>))]
+        [JsonSerializable(typeof(List<short?>))]
+        [JsonSerializable(typeof(Queue<short?>))]
+        [JsonSerializable(typeof(ImmutableList<short?>))]
+        [JsonSerializable(typeof(int?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<int?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<int?>))]
+        [JsonSerializable(typeof(IEnumerable<int?>))]
+        [JsonSerializable(typeof(Collection<int?>))]
+        [JsonSerializable(typeof(HashSet<int?>))]
+        [JsonSerializable(typeof(List<int?>))]
+        [JsonSerializable(typeof(Queue<int?>))]
+        [JsonSerializable(typeof(ImmutableList<int?>))]
+        [JsonSerializable(typeof(long?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<long?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<long?>))]
+        [JsonSerializable(typeof(IEnumerable<long?>))]
+        [JsonSerializable(typeof(Collection<long?>))]
+        [JsonSerializable(typeof(HashSet<long?>))]
+        [JsonSerializable(typeof(List<long?>))]
+        [JsonSerializable(typeof(Queue<long?>))]
+        [JsonSerializable(typeof(ImmutableList<long?>))]
+        [JsonSerializable(typeof(ushort?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ushort?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ushort?>))]
+        [JsonSerializable(typeof(IEnumerable<ushort?>))]
+        [JsonSerializable(typeof(Collection<ushort?>))]
+        [JsonSerializable(typeof(HashSet<ushort?>))]
+        [JsonSerializable(typeof(List<ushort?>))]
+        [JsonSerializable(typeof(Queue<ushort?>))]
+        [JsonSerializable(typeof(ImmutableList<ushort?>))]
+        [JsonSerializable(typeof(uint?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<uint?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<uint?>))]
+        [JsonSerializable(typeof(IEnumerable<uint?>))]
+        [JsonSerializable(typeof(Collection<uint?>))]
+        [JsonSerializable(typeof(HashSet<uint?>))]
+        [JsonSerializable(typeof(List<uint?>))]
+        [JsonSerializable(typeof(Queue<uint?>))]
+        [JsonSerializable(typeof(ImmutableList<uint?>))]
+        [JsonSerializable(typeof(ulong?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ulong?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ulong?>))]
+        [JsonSerializable(typeof(IEnumerable<ulong?>))]
+        [JsonSerializable(typeof(Collection<ulong?>))]
+        [JsonSerializable(typeof(HashSet<ulong?>))]
+        [JsonSerializable(typeof(List<ulong?>))]
+        [JsonSerializable(typeof(Queue<ulong?>))]
+        [JsonSerializable(typeof(ImmutableList<ulong?>))]
+        [JsonSerializable(typeof(float?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<float?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<float?>))]
+        [JsonSerializable(typeof(IEnumerable<float?>))]
+        [JsonSerializable(typeof(Collection<float?>))]
+        [JsonSerializable(typeof(HashSet<float?>))]
+        [JsonSerializable(typeof(List<float?>))]
+        [JsonSerializable(typeof(Queue<float?>))]
+        [JsonSerializable(typeof(ImmutableList<float?>))]
+        [JsonSerializable(typeof(double?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<double?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<double?>))]
+        [JsonSerializable(typeof(IEnumerable<double?>))]
+        [JsonSerializable(typeof(Collection<double?>))]
+        [JsonSerializable(typeof(HashSet<double?>))]
+        [JsonSerializable(typeof(List<double?>))]
+        [JsonSerializable(typeof(Queue<double?>))]
+        [JsonSerializable(typeof(ImmutableList<double?>))]
+        [JsonSerializable(typeof(decimal?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<decimal?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<decimal?>))]
+        [JsonSerializable(typeof(IEnumerable<decimal?>))]
+        [JsonSerializable(typeof(Collection<decimal?>))]
+        [JsonSerializable(typeof(HashSet<decimal?>))]
+        [JsonSerializable(typeof(List<decimal?>))]
+        [JsonSerializable(typeof(Queue<decimal?>))]
+        [JsonSerializable(typeof(ImmutableList<decimal?>))]
+        [JsonSerializable(typeof(IDictionary))]
+        [JsonSerializable(typeof(Dictionary<string, object>))]
+        [JsonSerializable(typeof(Dictionary<string, ulong>))]
+        [JsonSerializable(typeof(ConcurrentDictionary<string, ulong>))]
+        [JsonSerializable(typeof(IDictionary<string, ulong>))]
+        [JsonSerializable(typeof(GenericIDictionaryWrapper<string, ulong>))]
+        [JsonSerializable(typeof(ImmutableDictionary<string, ulong>))]
+        [JsonSerializable(typeof(IReadOnlyDictionary<string, ulong>))]
+        [JsonSerializable(typeof(Dictionary<string, float>))]
+        [JsonSerializable(typeof(ConcurrentDictionary<string, float>))]
+        [JsonSerializable(typeof(IDictionary<string, float>))]
+        [JsonSerializable(typeof(GenericIDictionaryWrapper<string, float>))]
+        [JsonSerializable(typeof(ImmutableDictionary<string, float>))]
+        [JsonSerializable(typeof(IReadOnlyDictionary<string, float>))]
+        [JsonSerializable(typeof(Dictionary<string, double>))]
+        [JsonSerializable(typeof(ConcurrentDictionary<string, double>))]
+        [JsonSerializable(typeof(IDictionary<string, double>))]
+        [JsonSerializable(typeof(GenericIDictionaryWrapper<string, double>))]
+        [JsonSerializable(typeof(ImmutableDictionary<string, double>))]
+        [JsonSerializable(typeof(IReadOnlyDictionary<string, double>))]
+        [JsonSerializable(typeof(Dictionary<ulong, ulong>))]
+        [JsonSerializable(typeof(Dictionary<float, float>))]
+        [JsonSerializable(typeof(Dictionary<double, double>))]
+        [JsonSerializable(typeof(SortedList))]
+        internal sealed partial class NumberHandlingTestsContext_Metadata : JsonSerializerContext
+        {
+        }
+    }
+
+    public sealed partial class NumberHandlingTests_Default : NumberHandlingTests
+    {
+        public NumberHandlingTests_Default()
+            : base(new StringSerializerWrapper(NumberHandlingTestsContext_Default.Default))
+        {
+        }
+
+        [JsonSerializable(typeof(Class_With_BoxedNumbers))]
+        [JsonSerializable(typeof(Class_With_ListsOfBoxedNumbers))]
+        [JsonSerializable(typeof(Class_With_BoxedNonNumbers))]
+        [JsonSerializable(typeof(Class_With_ListsOfBoxedNonNumbers))]
+        [JsonSerializable(typeof(ClassWithNumbers))]
+        [JsonSerializable(typeof(ClassWith_StrictAttribute))]
+        [JsonSerializable(typeof(ClassWith_ReadAsStringAttribute))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<int[]>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<ConcurrentQueue<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<GenericICollectionWrapper<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<IEnumerable<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<Collection<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<ImmutableList<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<HashSet<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<List<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<IList<int>>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<IList>))]
+        [JsonSerializable(typeof(ClassWithSimpleCollectionProperty<Queue<int>>))]
+        [JsonSerializable(typeof(ClassWithComplexListProperty))]
+        [JsonSerializable(typeof(ClassWith_AttributeOnComplexListProperty))]
+        [JsonSerializable(typeof(ClassWith_AttributeOnComplexDictionaryProperty))]
+        [JsonSerializable(typeof(ClassWithSimpleDictionaryProperty))]
+        [JsonSerializable(typeof(ClassWithComplexDictionaryProperty))]
+        [JsonSerializable(typeof(MyCustomList))]
+        [JsonSerializable(typeof(ClassWithCustomList))]
+        [JsonSerializable(typeof(MyCustomDictionary))]
+        [JsonSerializable(typeof(ClassWithCustomDictionary))]
+        [JsonSerializable(typeof(AttributeAppliedToFirstLevelProp))]
+        [JsonSerializable(typeof(NonNumberType))]
+        [JsonSerializable(typeof(MyCustomListWrapper))]
+        [JsonSerializable(typeof(MyCustomDictionaryWrapper))]
+        [JsonSerializable(typeof(ClassWith_NumberHandlingOn_ObjectProperty))]
+        [JsonSerializable(typeof(ClassWith_NumberHandlingOn_Property_WithCustomConverter))]
+        [JsonSerializable(typeof(ClassWith_NumberHandlingOn_Type_WithCustomConverter))]
+        [JsonSerializable(typeof(Class_With_NullableUInt64_And_Float))]
+        [JsonSerializable(typeof(MyClassWithNumbers))]
+        [JsonSerializable(typeof(MyClassWithNumbers_PropsHasAttribute))]
+        [JsonSerializable(typeof(ClassWith_Attribute_OnNumber))]
+        [JsonSerializable(typeof(Type_AllowFloatConstants))]
+        [JsonSerializable(typeof(ClassWith_LooseAttribute))]
+        [JsonSerializable(typeof(ClassWith_Attribute_On_TypeAndMember))]
+        [JsonSerializable(typeof(ClassWithListPropAndAttribute))]
+        [JsonSerializable(typeof(ClassWithDictPropAndAttribute))]
+        [JsonSerializable(typeof(ClassWithListPropAndAttribute_ConverterOnProp))]
+        [JsonSerializable(typeof(ClassWithDictPropAndAttribute_ConverterOnProp))]
+        [JsonSerializable(typeof(PlainClassWithList))]
+        [JsonSerializable(typeof(StructWithNumbers))]
+        [JsonSerializable(typeof(DateTime))]
+        [JsonSerializable(typeof(Guid?))]
+        [JsonSerializable(typeof(byte))]
+        [JsonSerializable(typeof(sbyte))]
+        [JsonSerializable(typeof(short))]
+        [JsonSerializable(typeof(int))]
+        [JsonSerializable(typeof(long))]
+        [JsonSerializable(typeof(ushort))]
+        [JsonSerializable(typeof(uint))]
+        [JsonSerializable(typeof(ulong))]
+        [JsonSerializable(typeof(float))]
+        [JsonSerializable(typeof(double))]
+        [JsonSerializable(typeof(decimal))]
+        [JsonSerializable(typeof(byte?))]
+        [JsonSerializable(typeof(sbyte?))]
+        [JsonSerializable(typeof(short?))]
+        [JsonSerializable(typeof(int?))]
+        [JsonSerializable(typeof(long?))]
+        [JsonSerializable(typeof(ushort?))]
+        [JsonSerializable(typeof(uint?))]
+        [JsonSerializable(typeof(ulong?))]
+        [JsonSerializable(typeof(float?))]
+        [JsonSerializable(typeof(double?))]
+        [JsonSerializable(typeof(decimal?))]
+        [JsonSerializable(typeof(IEnumerable<string>))]
+        [JsonSerializable(typeof(List<float>))]
+        [JsonSerializable(typeof(List<double>))]
+        [JsonSerializable(typeof(List<decimal>))]
+        [JsonSerializable(typeof(KeyValuePair<ulong?, List<float>>))]
+        [JsonSerializable(typeof(Dictionary<int, float>))]
+        [JsonSerializable(typeof(Dictionary<string, ulong>))]
+        [JsonSerializable(typeof(JsonElement))]
+        [JsonSerializable(typeof(Queue))]
+        [JsonSerializable(typeof(WrapperForIList))]
+        [JsonSerializable(typeof(byte[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<byte>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<byte>))]
+        [JsonSerializable(typeof(IEnumerable<byte>))]
+        [JsonSerializable(typeof(Collection<byte>))]
+        [JsonSerializable(typeof(HashSet<byte>))]
+        [JsonSerializable(typeof(List<byte>))]
+        [JsonSerializable(typeof(Queue<byte>))]
+        [JsonSerializable(typeof(ImmutableList<byte>))]
+        [JsonSerializable(typeof(sbyte[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<sbyte>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<sbyte>))]
+        [JsonSerializable(typeof(IEnumerable<sbyte>))]
+        [JsonSerializable(typeof(Collection<sbyte>))]
+        [JsonSerializable(typeof(HashSet<sbyte>))]
+        [JsonSerializable(typeof(List<sbyte>))]
+        [JsonSerializable(typeof(Queue<sbyte>))]
+        [JsonSerializable(typeof(ImmutableList<sbyte>))]
+        [JsonSerializable(typeof(short[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<short>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<short>))]
+        [JsonSerializable(typeof(IEnumerable<short>))]
+        [JsonSerializable(typeof(Collection<short>))]
+        [JsonSerializable(typeof(HashSet<short>))]
+        [JsonSerializable(typeof(List<short>))]
+        [JsonSerializable(typeof(Queue<short>))]
+        [JsonSerializable(typeof(ImmutableList<short>))]
+        [JsonSerializable(typeof(int[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<int>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<int>))]
+        [JsonSerializable(typeof(IEnumerable<int>))]
+        [JsonSerializable(typeof(Collection<int>))]
+        [JsonSerializable(typeof(HashSet<int>))]
+        [JsonSerializable(typeof(List<int>))]
+        [JsonSerializable(typeof(Queue<int>))]
+        [JsonSerializable(typeof(ImmutableList<int>))]
+        [JsonSerializable(typeof(long[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<long>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<long>))]
+        [JsonSerializable(typeof(IEnumerable<long>))]
+        [JsonSerializable(typeof(Collection<long>))]
+        [JsonSerializable(typeof(HashSet<long>))]
+        [JsonSerializable(typeof(List<long>))]
+        [JsonSerializable(typeof(Queue<long>))]
+        [JsonSerializable(typeof(ImmutableList<long>))]
+        [JsonSerializable(typeof(ushort[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ushort>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ushort>))]
+        [JsonSerializable(typeof(IEnumerable<ushort>))]
+        [JsonSerializable(typeof(Collection<ushort>))]
+        [JsonSerializable(typeof(HashSet<ushort>))]
+        [JsonSerializable(typeof(List<ushort>))]
+        [JsonSerializable(typeof(Queue<ushort>))]
+        [JsonSerializable(typeof(ImmutableList<ushort>))]
+        [JsonSerializable(typeof(uint[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<uint>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<uint>))]
+        [JsonSerializable(typeof(IEnumerable<uint>))]
+        [JsonSerializable(typeof(Collection<uint>))]
+        [JsonSerializable(typeof(HashSet<uint>))]
+        [JsonSerializable(typeof(List<uint>))]
+        [JsonSerializable(typeof(Queue<uint>))]
+        [JsonSerializable(typeof(ImmutableList<uint>))]
+        [JsonSerializable(typeof(short?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<short?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<short?>))]
+        [JsonSerializable(typeof(IEnumerable<short?>))]
+        [JsonSerializable(typeof(Collection<short?>))]
+        [JsonSerializable(typeof(HashSet<short?>))]
+        [JsonSerializable(typeof(List<short?>))]
+        [JsonSerializable(typeof(Queue<short?>))]
+        [JsonSerializable(typeof(ImmutableList<short?>))]
+        [JsonSerializable(typeof(int?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<int?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<int?>))]
+        [JsonSerializable(typeof(IEnumerable<int?>))]
+        [JsonSerializable(typeof(Collection<int?>))]
+        [JsonSerializable(typeof(HashSet<int?>))]
+        [JsonSerializable(typeof(List<int?>))]
+        [JsonSerializable(typeof(Queue<int?>))]
+        [JsonSerializable(typeof(ImmutableList<int?>))]
+        [JsonSerializable(typeof(long?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<long?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<long?>))]
+        [JsonSerializable(typeof(IEnumerable<long?>))]
+        [JsonSerializable(typeof(Collection<long?>))]
+        [JsonSerializable(typeof(HashSet<long?>))]
+        [JsonSerializable(typeof(List<long?>))]
+        [JsonSerializable(typeof(Queue<long?>))]
+        [JsonSerializable(typeof(ImmutableList<long?>))]
+        [JsonSerializable(typeof(ushort?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ushort?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ushort?>))]
+        [JsonSerializable(typeof(IEnumerable<ushort?>))]
+        [JsonSerializable(typeof(Collection<ushort?>))]
+        [JsonSerializable(typeof(HashSet<ushort?>))]
+        [JsonSerializable(typeof(List<ushort?>))]
+        [JsonSerializable(typeof(Queue<ushort?>))]
+        [JsonSerializable(typeof(ImmutableList<ushort?>))]
+        [JsonSerializable(typeof(uint?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<uint?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<uint?>))]
+        [JsonSerializable(typeof(IEnumerable<uint?>))]
+        [JsonSerializable(typeof(Collection<uint?>))]
+        [JsonSerializable(typeof(HashSet<uint?>))]
+        [JsonSerializable(typeof(List<uint?>))]
+        [JsonSerializable(typeof(Queue<uint?>))]
+        [JsonSerializable(typeof(ImmutableList<uint?>))]
+        [JsonSerializable(typeof(ulong[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ulong>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ulong>))]
+        [JsonSerializable(typeof(IEnumerable<ulong>))]
+        [JsonSerializable(typeof(Collection<ulong>))]
+        [JsonSerializable(typeof(HashSet<ulong>))]
+        [JsonSerializable(typeof(List<ulong>))]
+        [JsonSerializable(typeof(Queue<ulong>))]
+        [JsonSerializable(typeof(ImmutableList<ulong>))]
+        [JsonSerializable(typeof(float[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<float>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<float>))]
+        [JsonSerializable(typeof(IEnumerable<float>))]
+        [JsonSerializable(typeof(Collection<float>))]
+        [JsonSerializable(typeof(HashSet<float>))]
+        [JsonSerializable(typeof(List<float>))]
+        [JsonSerializable(typeof(Queue<float>))]
+        [JsonSerializable(typeof(ImmutableList<float>))]
+        [JsonSerializable(typeof(double[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<double>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<double>))]
+        [JsonSerializable(typeof(IEnumerable<double>))]
+        [JsonSerializable(typeof(Collection<double>))]
+        [JsonSerializable(typeof(HashSet<double>))]
+        [JsonSerializable(typeof(List<double>))]
+        [JsonSerializable(typeof(Queue<double>))]
+        [JsonSerializable(typeof(ImmutableList<double>))]
+        [JsonSerializable(typeof(decimal[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<decimal>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<decimal>))]
+        [JsonSerializable(typeof(IEnumerable<decimal>))]
+        [JsonSerializable(typeof(Collection<decimal>))]
+        [JsonSerializable(typeof(HashSet<decimal>))]
+        [JsonSerializable(typeof(List<decimal>))]
+        [JsonSerializable(typeof(Queue<decimal>))]
+        [JsonSerializable(typeof(ImmutableList<decimal>))]
+        [JsonSerializable(typeof(List<byte>))]
+        [JsonSerializable(typeof(List<sbyte>))]
+        [JsonSerializable(typeof(List<short>))]
+        [JsonSerializable(typeof(List<int>))]
+        [JsonSerializable(typeof(List<long>))]
+        [JsonSerializable(typeof(List<ushort>))]
+        [JsonSerializable(typeof(List<uint>))]
+        [JsonSerializable(typeof(List<ulong>))]
+        [JsonSerializable(typeof(List<float>))]
+        [JsonSerializable(typeof(List<double>))]
+        [JsonSerializable(typeof(List<decimal>))]
+        [JsonSerializable(typeof(List<byte?>))]
+        [JsonSerializable(typeof(List<sbyte?>))]
+        [JsonSerializable(typeof(List<short?>))]
+        [JsonSerializable(typeof(List<int?>))]
+        [JsonSerializable(typeof(List<long?>))]
+        [JsonSerializable(typeof(List<ushort?>))]
+        [JsonSerializable(typeof(List<uint?>))]
+        [JsonSerializable(typeof(List<ulong?>))]
+        [JsonSerializable(typeof(List<float?>))]
+        [JsonSerializable(typeof(List<double?>))]
+        [JsonSerializable(typeof(List<decimal?>))]
+        [JsonSerializable(typeof(Hashtable))]
+        [JsonSerializable(typeof(byte?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<byte?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<byte?>))]
+        [JsonSerializable(typeof(IEnumerable<byte?>))]
+        [JsonSerializable(typeof(Collection<byte?>))]
+        [JsonSerializable(typeof(HashSet<byte?>))]
+        [JsonSerializable(typeof(List<byte?>))]
+        [JsonSerializable(typeof(Queue<byte?>))]
+        [JsonSerializable(typeof(ImmutableList<byte?>))]
+        [JsonSerializable(typeof(sbyte?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<sbyte?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<sbyte?>))]
+        [JsonSerializable(typeof(IEnumerable<sbyte?>))]
+        [JsonSerializable(typeof(Collection<sbyte?>))]
+        [JsonSerializable(typeof(HashSet<sbyte?>))]
+        [JsonSerializable(typeof(List<sbyte?>))]
+        [JsonSerializable(typeof(Queue<sbyte?>))]
+        [JsonSerializable(typeof(ImmutableList<sbyte?>))]
+        [JsonSerializable(typeof(short?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<short?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<short?>))]
+        [JsonSerializable(typeof(IEnumerable<short?>))]
+        [JsonSerializable(typeof(Collection<short?>))]
+        [JsonSerializable(typeof(HashSet<short?>))]
+        [JsonSerializable(typeof(List<short?>))]
+        [JsonSerializable(typeof(Queue<short?>))]
+        [JsonSerializable(typeof(ImmutableList<short?>))]
+        [JsonSerializable(typeof(int?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<int?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<int?>))]
+        [JsonSerializable(typeof(IEnumerable<int?>))]
+        [JsonSerializable(typeof(Collection<int?>))]
+        [JsonSerializable(typeof(HashSet<int?>))]
+        [JsonSerializable(typeof(List<int?>))]
+        [JsonSerializable(typeof(Queue<int?>))]
+        [JsonSerializable(typeof(ImmutableList<int?>))]
+        [JsonSerializable(typeof(long?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<long?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<long?>))]
+        [JsonSerializable(typeof(IEnumerable<long?>))]
+        [JsonSerializable(typeof(Collection<long?>))]
+        [JsonSerializable(typeof(HashSet<long?>))]
+        [JsonSerializable(typeof(List<long?>))]
+        [JsonSerializable(typeof(Queue<long?>))]
+        [JsonSerializable(typeof(ImmutableList<long?>))]
+        [JsonSerializable(typeof(ushort?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ushort?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ushort?>))]
+        [JsonSerializable(typeof(IEnumerable<ushort?>))]
+        [JsonSerializable(typeof(Collection<ushort?>))]
+        [JsonSerializable(typeof(HashSet<ushort?>))]
+        [JsonSerializable(typeof(List<ushort?>))]
+        [JsonSerializable(typeof(Queue<ushort?>))]
+        [JsonSerializable(typeof(ImmutableList<ushort?>))]
+        [JsonSerializable(typeof(uint?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<uint?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<uint?>))]
+        [JsonSerializable(typeof(IEnumerable<uint?>))]
+        [JsonSerializable(typeof(Collection<uint?>))]
+        [JsonSerializable(typeof(HashSet<uint?>))]
+        [JsonSerializable(typeof(List<uint?>))]
+        [JsonSerializable(typeof(Queue<uint?>))]
+        [JsonSerializable(typeof(ImmutableList<uint?>))]
+        [JsonSerializable(typeof(ulong?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<ulong?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<ulong?>))]
+        [JsonSerializable(typeof(IEnumerable<ulong?>))]
+        [JsonSerializable(typeof(Collection<ulong?>))]
+        [JsonSerializable(typeof(HashSet<ulong?>))]
+        [JsonSerializable(typeof(List<ulong?>))]
+        [JsonSerializable(typeof(Queue<ulong?>))]
+        [JsonSerializable(typeof(ImmutableList<ulong?>))]
+        [JsonSerializable(typeof(float?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<float?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<float?>))]
+        [JsonSerializable(typeof(IEnumerable<float?>))]
+        [JsonSerializable(typeof(Collection<float?>))]
+        [JsonSerializable(typeof(HashSet<float?>))]
+        [JsonSerializable(typeof(List<float?>))]
+        [JsonSerializable(typeof(Queue<float?>))]
+        [JsonSerializable(typeof(ImmutableList<float?>))]
+        [JsonSerializable(typeof(double?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<double?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<double?>))]
+        [JsonSerializable(typeof(IEnumerable<double?>))]
+        [JsonSerializable(typeof(Collection<double?>))]
+        [JsonSerializable(typeof(HashSet<double?>))]
+        [JsonSerializable(typeof(List<double?>))]
+        [JsonSerializable(typeof(Queue<double?>))]
+        [JsonSerializable(typeof(ImmutableList<double?>))]
+        [JsonSerializable(typeof(decimal?[]))]
+        [JsonSerializable(typeof(ConcurrentQueue<decimal?>))]
+        [JsonSerializable(typeof(GenericICollectionWrapper<decimal?>))]
+        [JsonSerializable(typeof(IEnumerable<decimal?>))]
+        [JsonSerializable(typeof(Collection<decimal?>))]
+        [JsonSerializable(typeof(HashSet<decimal?>))]
+        [JsonSerializable(typeof(List<decimal?>))]
+        [JsonSerializable(typeof(Queue<decimal?>))]
+        [JsonSerializable(typeof(ImmutableList<decimal?>))]
+        [JsonSerializable(typeof(IDictionary))]
+        [JsonSerializable(typeof(Dictionary<string, object>))]
+        [JsonSerializable(typeof(Dictionary<string, ulong>))]
+        [JsonSerializable(typeof(ConcurrentDictionary<string, ulong>))]
+        [JsonSerializable(typeof(IDictionary<string, ulong>))]
+        [JsonSerializable(typeof(GenericIDictionaryWrapper<string, ulong>))]
+        [JsonSerializable(typeof(ImmutableDictionary<string, ulong>))]
+        [JsonSerializable(typeof(IReadOnlyDictionary<string, ulong>))]
+        [JsonSerializable(typeof(Dictionary<string, float>))]
+        [JsonSerializable(typeof(ConcurrentDictionary<string, float>))]
+        [JsonSerializable(typeof(IDictionary<string, float>))]
+        [JsonSerializable(typeof(GenericIDictionaryWrapper<string, float>))]
+        [JsonSerializable(typeof(ImmutableDictionary<string, float>))]
+        [JsonSerializable(typeof(IReadOnlyDictionary<string, float>))]
+        [JsonSerializable(typeof(Dictionary<string, double>))]
+        [JsonSerializable(typeof(ConcurrentDictionary<string, double>))]
+        [JsonSerializable(typeof(IDictionary<string, double>))]
+        [JsonSerializable(typeof(GenericIDictionaryWrapper<string, double>))]
+        [JsonSerializable(typeof(ImmutableDictionary<string, double>))]
+        [JsonSerializable(typeof(IReadOnlyDictionary<string, double>))]
+        [JsonSerializable(typeof(Dictionary<ulong, ulong>))]
+        [JsonSerializable(typeof(Dictionary<float, float>))]
+        [JsonSerializable(typeof(Dictionary<double, double>))]
+        [JsonSerializable(typeof(SortedList))]
+        internal sealed partial class NumberHandlingTestsContext_Default : JsonSerializerContext
+        {
+        }
+    }
+}
index 4ff2b90..10ae6df 100644 (file)
@@ -54,6 +54,7 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(TypeWithValidationAttributes))]
     [JsonSerializable(typeof(TypeWithDerivedAttribute))]
     [JsonSerializable(typeof(PolymorphicClass))]
+    [JsonSerializable(typeof(PocoWithNumberHandlingAttr))]
     internal partial class SerializationContext : JsonSerializerContext, ITestContext
     {
         public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Serialization;
@@ -105,6 +106,7 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(TypeWithValidationAttributes), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(TypeWithDerivedAttribute), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(PolymorphicClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
+    [JsonSerializable(typeof(PocoWithNumberHandlingAttr), GenerationMode = JsonSourceGenerationMode.Serialization)]
     internal partial class SerializationWithPerTypeAttributeContext : JsonSerializerContext, ITestContext
     {
         public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Serialization;
@@ -157,6 +159,7 @@ namespace System.Text.Json.SourceGeneration.Tests
     [JsonSerializable(typeof(TypeWithValidationAttributes), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(TypeWithDerivedAttribute), GenerationMode = JsonSourceGenerationMode.Serialization)]
     [JsonSerializable(typeof(PolymorphicClass), GenerationMode = JsonSourceGenerationMode.Serialization)]
+    [JsonSerializable(typeof(PocoWithNumberHandlingAttr), GenerationMode = JsonSourceGenerationMode.Serialization)]
     internal partial class SerializationContextWithCamelCase : JsonSerializerContext, ITestContext
     {
         public JsonSourceGenerationMode JsonSourceGenerationMode => JsonSourceGenerationMode.Serialization;
index 704790d..5d7adae 100644 (file)
@@ -76,7 +76,9 @@
     <Compile Include="..\Common\JsonCreationHandlingTests.Enumerable.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\JsonCreationHandlingTests.Enumerable.cs" />
     <Compile Include="..\Common\JsonCreationHandlingTests.Generic.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\JsonCreationHandlingTests.Generic.cs" />
     <Compile Include="..\Common\JsonCreationHandlingTests.Object.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\JsonCreationHandlingTests.Object.cs" />
+    <Compile Include="..\Common\JsonNumberTestData.cs" Link="CommonTest\System\Text\Json\Tests\JsonNumberTestData" />
     <Compile Include="..\Common\NodeInteropTests.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\NodeInteropTests.cs" />
+    <Compile Include="..\Common\NumberHandlingTests.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\NumberHandlingTests.cs" />
     <Compile Include="..\Common\PropertyNameTests.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\PropertyNameTests.cs" />
     <Compile Include="..\Common\PropertyVisibilityTests.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\PropertyVisibilityTests.cs" />
     <Compile Include="..\Common\PropertyVisibilityTests.InitOnly.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\PropertyVisibilityTests.InitOnly.cs" />
     <Compile Include="Serialization\ReferenceHandlerTests.cs" />
     <Compile Include="Serialization\ReferenceHandlerTests.IgnoreCycles.cs" />
     <Compile Include="Serialization\NodeInteropTests.cs" />
+    <Compile Include="Serialization\NumberHandlingTests.cs" />
     <Compile Include="Serialization\PropertyNameTests.cs" />
     <Compile Include="Serialization\PropertyVisibilityTests.cs" />
     <Compile Include="Serialization\UnmappedMemberHandlingTests.cs" />
index eb4da1d..fc37676 100644 (file)
@@ -287,4 +287,10 @@ namespace System.Text.Json.SourceGeneration.Tests
         public ClassWithDictionaryProperty(Dictionary<string, object?> property) => DictionaryProperty = property;
         public Dictionary<string, object?> DictionaryProperty { get; }
     }
+
+    [JsonNumberHandling(JsonNumberHandling.WriteAsString)]
+    public class PocoWithNumberHandlingAttr
+    {
+        public int Id { get; set; }
+    }
 }
index a84b6a2..004020f 100644 (file)
@@ -17,14 +17,6 @@ namespace System.Text.Json
 {
     internal static partial class JsonTestHelper
     {
-#if NETCOREAPP
-        public const string DoubleFormatString = null;
-        public const string SingleFormatString = null;
-#else
-        public const string DoubleFormatString = "G17";
-        public const string SingleFormatString = "G9";
-#endif
-
         public static string NewtonsoftReturnStringHelper(TextReader reader)
         {
             var sb = new StringBuilder();
@@ -681,26 +673,6 @@ namespace System.Text.Json
             return arrayList;
         }
 
-        public static float NextFloat(Random random)
-        {
-            double mantissa = (random.NextDouble() * 2.0) - 1.0;
-            double exponent = Math.Pow(2.0, random.Next(-126, 128));
-            float value = (float)(mantissa * exponent);
-            return value;
-        }
-
-        public static double NextDouble(Random random, double minValue, double maxValue)
-        {
-            double value = random.NextDouble() * (maxValue - minValue) + minValue;
-            return value;
-        }
-
-        public static decimal NextDecimal(Random random, double minValue, double maxValue)
-        {
-            double value = random.NextDouble() * (maxValue - minValue) + minValue;
-            return (decimal)value;
-        }
-
         public static string GetCompactString(string jsonString)
         {
             using (var jsonReader = new JsonTextReader(new StringReader(jsonString)) { MaxDepth = null })
index 5f6f27c..1dfa1e1 100644 (file)
 ï»¿// 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.Collections.Immutable;
-using System.Collections.ObjectModel;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using System.Text.Encodings.Web;
-using System.Text.Json.Tests;
 using System.Threading.Tasks;
 using Xunit;
-using System.Runtime.InteropServices;
 
 namespace System.Text.Json.Serialization.Tests
 {
-    public static partial class NumberHandlingTests
+    public sealed partial class NumberHandlingTestsDynamic : NumberHandlingTests
     {
-        private static readonly JsonSerializerOptions s_optionReadFromStr = new JsonSerializerOptions
-        {
-            NumberHandling = JsonNumberHandling.AllowReadingFromString
-        };
-
-        private static readonly JsonSerializerOptions s_optionWriteAsStr = new JsonSerializerOptions
-        {
-            NumberHandling = JsonNumberHandling.WriteAsString
-        };
-
-        private static readonly JsonSerializerOptions s_optionReadAndWriteFromStr = new JsonSerializerOptions
-        {
-            NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString
-        };
-
-        private static readonly JsonSerializerOptions s_optionsAllowFloatConstants = new JsonSerializerOptions
-        {
-            NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals
-        };
-
-        private static readonly JsonSerializerOptions s_optionReadFromStrAllowFloatConstants = new JsonSerializerOptions
-        {
-            NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.AllowNamedFloatingPointLiterals
-        };
-
-        private static readonly JsonSerializerOptions s_optionWriteAsStrAllowFloatConstants = new JsonSerializerOptions
-        {
-            NumberHandling = JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowNamedFloatingPointLiterals
-        };
-
-        [Fact]
-        public static void Number_AsRootType_RoundTrip()
-        {
-            RunAsRootTypeTest(JsonNumberTestData.Bytes);
-            RunAsRootTypeTest(JsonNumberTestData.SBytes);
-            RunAsRootTypeTest(JsonNumberTestData.Shorts);
-            RunAsRootTypeTest(JsonNumberTestData.Ints);
-            RunAsRootTypeTest(JsonNumberTestData.Longs);
-            RunAsRootTypeTest(JsonNumberTestData.UShorts);
-            RunAsRootTypeTest(JsonNumberTestData.UInts);
-            RunAsRootTypeTest(JsonNumberTestData.ULongs);
-            RunAsRootTypeTest(JsonNumberTestData.Floats);
-            RunAsRootTypeTest(JsonNumberTestData.Doubles);
-            RunAsRootTypeTest(JsonNumberTestData.Decimals);
-            RunAsRootTypeTest(JsonNumberTestData.NullableBytes);
-            RunAsRootTypeTest(JsonNumberTestData.NullableSBytes);
-            RunAsRootTypeTest(JsonNumberTestData.NullableShorts);
-            RunAsRootTypeTest(JsonNumberTestData.NullableInts);
-            RunAsRootTypeTest(JsonNumberTestData.NullableLongs);
-            RunAsRootTypeTest(JsonNumberTestData.NullableUShorts);
-            RunAsRootTypeTest(JsonNumberTestData.NullableUInts);
-            RunAsRootTypeTest(JsonNumberTestData.NullableULongs);
-            RunAsRootTypeTest(JsonNumberTestData.NullableFloats);
-            RunAsRootTypeTest(JsonNumberTestData.NullableDoubles);
-            RunAsRootTypeTest(JsonNumberTestData.NullableDecimals);
-        }
-
-        private static void RunAsRootTypeTest<T>(List<T> numbers)
-        {
-            foreach (T number in numbers)
-            {
-                string numberAsString = GetNumberAsString(number);
-                string json = $"{numberAsString}";
-                string jsonWithNumberAsString = @$"""{numberAsString}""";
-                PerformAsRootTypeSerialization(number, json, jsonWithNumberAsString);
-            }
-        }
-
-        private static string GetNumberAsString<T>(T number)
-        {
-            return number switch
-            {
-                double @double => @double.ToString(JsonTestHelper.DoubleFormatString, CultureInfo.InvariantCulture),
-                float @float => @float.ToString(JsonTestHelper.SingleFormatString, CultureInfo.InvariantCulture),
-                decimal @decimal => @decimal.ToString(CultureInfo.InvariantCulture),
-                _ => number.ToString()
-            };
-        }
-
-        private static void PerformAsRootTypeSerialization<T>(T number, string jsonWithNumberAsNumber, string jsonWithNumberAsString)
-        {
-            // Option: read from string
-
-            // Deserialize
-            Assert.Equal(number, JsonSerializer.Deserialize<T>(jsonWithNumberAsNumber, s_optionReadFromStr));
-            Assert.Equal(number, JsonSerializer.Deserialize<T>(jsonWithNumberAsString, s_optionReadFromStr));
-
-            // Serialize
-            Assert.Equal(jsonWithNumberAsNumber, JsonSerializer.Serialize(number, s_optionReadFromStr));
-
-            // Option: write as string
-
-            // Deserialize
-            Assert.Equal(number, JsonSerializer.Deserialize<T>(jsonWithNumberAsNumber, s_optionWriteAsStr));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<T>(jsonWithNumberAsString, s_optionWriteAsStr));
-
-            // Serialize
-            Assert.Equal(jsonWithNumberAsString, JsonSerializer.Serialize(number, s_optionWriteAsStr));
-
-            // Option: read and write from/to string
-
-            // Deserialize
-            Assert.Equal(number, JsonSerializer.Deserialize<T>(jsonWithNumberAsNumber, s_optionReadAndWriteFromStr));
-            Assert.Equal(number, JsonSerializer.Deserialize<T>(jsonWithNumberAsString, s_optionReadAndWriteFromStr));
-
-            // Serialize
-            Assert.Equal(jsonWithNumberAsString, JsonSerializer.Serialize(number, s_optionReadAndWriteFromStr));
-        }
-
-        [Fact]
-        public static void Number_AsBoxed_RootType()
-        {
-            string numberAsString = @"""2""";
-
-            int @int = 2;
-            float @float = 2;
-            int? nullableInt = 2;
-            float? nullableFloat = 2;
-
-            Assert.Equal(numberAsString, JsonSerializer.Serialize((object)@int, s_optionReadAndWriteFromStr));
-            Assert.Equal(numberAsString, JsonSerializer.Serialize((object)@float, s_optionReadAndWriteFromStr));
-            Assert.Equal(numberAsString, JsonSerializer.Serialize((object)nullableInt, s_optionReadAndWriteFromStr));
-            Assert.Equal(numberAsString, JsonSerializer.Serialize((object)nullableFloat, s_optionReadAndWriteFromStr));
-
-            Assert.Equal(2, (int)JsonSerializer.Deserialize(numberAsString, typeof(int), s_optionReadAndWriteFromStr));
-            Assert.Equal(2, (float)JsonSerializer.Deserialize(numberAsString, typeof(float), s_optionReadAndWriteFromStr));
-            Assert.Equal(2, (int?)JsonSerializer.Deserialize(numberAsString, typeof(int?), s_optionReadAndWriteFromStr));
-            Assert.Equal(2, (float?)JsonSerializer.Deserialize(numberAsString, typeof(float?), s_optionReadAndWriteFromStr));
-        }
-
-        [Fact]
-        public static void Number_AsBoxed_Property()
-        {
-            int @int = 1;
-            float? nullableFloat = 2;
-
-            string expected = @"{""MyInt"":""1"",""MyNullableFloat"":""2""}";
-
-            var obj = new Class_With_BoxedNumbers
-            {
-                MyInt = @int,
-                MyNullableFloat = nullableFloat
-            };
-
-            string serialized = JsonSerializer.Serialize(obj);
-            JsonTestHelper.AssertJsonEqual(expected, serialized);
-
-            obj = JsonSerializer.Deserialize<Class_With_BoxedNumbers>(serialized);
-
-            JsonElement el = Assert.IsType<JsonElement>(obj.MyInt);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal("1", el.GetString());
-
-            el = Assert.IsType<JsonElement>(obj.MyNullableFloat);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal("2", el.GetString());
-        }
-
-        public class Class_With_BoxedNumbers
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public object MyInt { get; set; }
-
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public object MyNullableFloat { get; set; }
-        }
-
-        [Fact]
-        public static void Number_AsBoxed_CollectionRootType_Element()
-        {
-            int @int = 1;
-            float? nullableFloat = 2;
-
-            string expected = @"[""1""]";
-
-            var obj = new List<object> { @int };
-            string serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-            Assert.Equal(expected, serialized);
-
-            obj = JsonSerializer.Deserialize<List<object>>(serialized, s_optionReadAndWriteFromStr);
-
-            JsonElement el = Assert.IsType<JsonElement>(obj[0]);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal("1", el.GetString());
-
-            expected = @"[""2""]";
-
-            IList obj2 = new object[] { nullableFloat };
-            serialized = JsonSerializer.Serialize(obj2, s_optionReadAndWriteFromStr);
-            Assert.Equal(expected, serialized);
-
-            obj2 = JsonSerializer.Deserialize<IList>(serialized, s_optionReadAndWriteFromStr);
-
-            el = Assert.IsType<JsonElement>(obj2[0]);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal("2", el.GetString());
-        }
-
-        [Fact]
-        public static void Number_AsBoxed_CollectionProperty_Element()
-        {
-            int @int = 2;
-            float? nullableFloat = 2;
-
-            string expected = @"{""MyInts"":[""2""],""MyNullableFloats"":[""2""]}";
-
-            var obj = new Class_With_ListsOfBoxedNumbers
-            {
-                MyInts = new List<object> { @int },
-                MyNullableFloats = new object[] { nullableFloat }
-            };
-
-            string serialized = JsonSerializer.Serialize(obj);
-            JsonTestHelper.AssertJsonEqual(expected, serialized);
-
-            obj = JsonSerializer.Deserialize<Class_With_ListsOfBoxedNumbers>(serialized);
-
-            JsonElement el = Assert.IsType<JsonElement>(obj.MyInts[0]);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal("2", el.GetString());
-
-            el = Assert.IsType<JsonElement>(obj.MyNullableFloats[0]);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal("2", el.GetString());
-        }
-
-        public class Class_With_ListsOfBoxedNumbers
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public List<object> MyInts { get; set; }
-
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public IList MyNullableFloats { get; set; }
-        }
-
-        [Fact]
-        public static void NonNumber_AsBoxed_Property()
-        {
-            DateTime dateTime = DateTimeTestHelpers.FixedDateTimeValue;
-            Guid? nullableGuid = Guid.NewGuid();
-
-            string expected = @$"{{""MyDateTime"":{JsonSerializer.Serialize(dateTime)},""MyNullableGuid"":{JsonSerializer.Serialize(nullableGuid)}}}";
-
-            var obj = new Class_With_BoxedNonNumbers
-            {
-                MyDateTime = dateTime,
-                MyNullableGuid = nullableGuid
-            };
-
-            string serialized = JsonSerializer.Serialize(obj);
-            JsonTestHelper.AssertJsonEqual(expected, serialized);
-
-            obj = JsonSerializer.Deserialize<Class_With_BoxedNonNumbers>(serialized);
-
-            JsonElement el = Assert.IsType<JsonElement>(obj.MyDateTime);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal(dateTime, el.GetDateTime());
-
-            el = Assert.IsType<JsonElement>(obj.MyNullableGuid);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal(nullableGuid.Value, el.GetGuid());
-        }
-
-        public class Class_With_BoxedNonNumbers
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public object MyDateTime { get; set; }
-
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public object MyNullableGuid { get; set; }
-        }
-
-        [Fact]
-        public static void NonNumber_AsBoxed_CollectionRootType_Element()
-        {
-            DateTime dateTime = DateTimeTestHelpers.FixedDateTimeValue;
-            Guid? nullableGuid = Guid.NewGuid();
-
-            string expected = @$"[{JsonSerializer.Serialize(dateTime)}]";
-
-            var obj = new List<object> { dateTime };
-            string serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-            Assert.Equal(expected, serialized);
-
-            obj = JsonSerializer.Deserialize<List<object>>(serialized, s_optionReadAndWriteFromStr);
-
-            JsonElement el = Assert.IsType<JsonElement>(obj[0]);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal(dateTime, el.GetDateTime());
-
-            expected = @$"[{JsonSerializer.Serialize(nullableGuid)}]";
-
-            IList obj2 = new object[] { nullableGuid };
-            serialized = JsonSerializer.Serialize(obj2, s_optionReadAndWriteFromStr);
-            Assert.Equal(expected, serialized);
-
-            obj2 = JsonSerializer.Deserialize<IList>(serialized, s_optionReadAndWriteFromStr);
-
-            el = Assert.IsType<JsonElement>(obj2[0]);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal(nullableGuid.Value, el.GetGuid());
-        }
-
-        [Fact]
-        public static void NonNumber_AsBoxed_CollectionProperty_Element()
-        {
-            DateTime dateTime = DateTimeTestHelpers.FixedDateTimeValue;
-            Guid? nullableGuid = Guid.NewGuid();
-
-            string expected = @$"{{""MyDateTimes"":[{JsonSerializer.Serialize(dateTime)}],""MyNullableGuids"":[{JsonSerializer.Serialize(nullableGuid)}]}}";
-
-            var obj = new Class_With_ListsOfBoxedNonNumbers
-            {
-                MyDateTimes = new List<object> { dateTime },
-                MyNullableGuids = new object[] { nullableGuid }
-            };
-
-            string serialized = JsonSerializer.Serialize(obj);
-            JsonTestHelper.AssertJsonEqual(expected, serialized);
-
-            obj = JsonSerializer.Deserialize<Class_With_ListsOfBoxedNonNumbers>(serialized);
-
-            JsonElement el = Assert.IsType<JsonElement>(obj.MyDateTimes[0]);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal(dateTime, el.GetDateTime());
-
-            el = Assert.IsType<JsonElement>(obj.MyNullableGuids[0]);
-            Assert.Equal(JsonValueKind.String, el.ValueKind);
-            Assert.Equal(nullableGuid, el.GetGuid());
-        }
-
-        public class Class_With_ListsOfBoxedNonNumbers
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public List<object> MyDateTimes { get; set; }
-
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public IList MyNullableGuids { get; set; }
-        }
-
-        [Fact]
-        public static void Number_AsCollectionElement_RoundTrip()
-        {
-            RunAsCollectionElementTest(JsonNumberTestData.Bytes);
-            RunAsCollectionElementTest(JsonNumberTestData.SBytes);
-            RunAsCollectionElementTest(JsonNumberTestData.Shorts);
-            RunAsCollectionElementTest(JsonNumberTestData.Ints);
-            RunAsCollectionElementTest(JsonNumberTestData.Longs);
-            RunAsCollectionElementTest(JsonNumberTestData.UShorts);
-            RunAsCollectionElementTest(JsonNumberTestData.UInts);
-            RunAsCollectionElementTest(JsonNumberTestData.ULongs);
-            RunAsCollectionElementTest(JsonNumberTestData.Floats);
-            RunAsCollectionElementTest(JsonNumberTestData.Doubles);
-            RunAsCollectionElementTest(JsonNumberTestData.Decimals);
-
-            // https://github.com/dotnet/runtime/issues/66220
-            if (!PlatformDetection.IsAppleMobile)
-            {
-                RunAsCollectionElementTest(JsonNumberTestData.NullableBytes);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableSBytes);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableShorts);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableInts);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableLongs);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableUShorts);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableUInts);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableULongs);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableFloats);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableDoubles);
-                RunAsCollectionElementTest(JsonNumberTestData.NullableDecimals);
-            }
-        }
-
-        private static void RunAsCollectionElementTest<T>(List<T> numbers)
-        {
-            StringBuilder jsonBuilder_NumbersAsNumbers = new StringBuilder();
-            StringBuilder jsonBuilder_NumbersAsStrings = new StringBuilder();
-            StringBuilder jsonBuilder_NumbersAsNumbersAndStrings = new StringBuilder();
-            StringBuilder jsonBuilder_NumbersAsNumbersAndStrings_Alternate = new StringBuilder();
-            bool asNumber = false;
-
-            jsonBuilder_NumbersAsNumbers.Append("[");
-            jsonBuilder_NumbersAsStrings.Append("[");
-            jsonBuilder_NumbersAsNumbersAndStrings.Append("[");
-            jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Append("[");
-
-            foreach (T number in numbers)
-            {
-                string numberAsString = GetNumberAsString(number);
-
-                string jsonWithNumberAsString = @$"""{numberAsString}""";
-
-                jsonBuilder_NumbersAsNumbers.Append($"{numberAsString},");
-                jsonBuilder_NumbersAsStrings.Append($"{jsonWithNumberAsString},");
-                jsonBuilder_NumbersAsNumbersAndStrings.Append(asNumber
-                    ? $"{numberAsString},"
-                    : $"{jsonWithNumberAsString},");
-                jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Append(!asNumber
-                    ? $"{numberAsString},"
-                    : $"{jsonWithNumberAsString},");
-
-                asNumber = !asNumber;
-            }
-
-            jsonBuilder_NumbersAsNumbers.Remove(jsonBuilder_NumbersAsNumbers.Length - 1, 1);
-            jsonBuilder_NumbersAsStrings.Remove(jsonBuilder_NumbersAsStrings.Length - 1, 1);
-            jsonBuilder_NumbersAsNumbersAndStrings.Remove(jsonBuilder_NumbersAsNumbersAndStrings.Length - 1, 1);
-            jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Remove(jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Length - 1, 1);
-
-            jsonBuilder_NumbersAsNumbers.Append("]");
-            jsonBuilder_NumbersAsStrings.Append("]");
-            jsonBuilder_NumbersAsNumbersAndStrings.Append("]");
-            jsonBuilder_NumbersAsNumbersAndStrings_Alternate.Append("]");
-
-            string jsonNumbersAsStrings = jsonBuilder_NumbersAsStrings.ToString();
-
-            PerformAsCollectionElementSerialization(
-                numbers,
-                jsonBuilder_NumbersAsNumbers.ToString(),
-                jsonNumbersAsStrings,
-                jsonBuilder_NumbersAsNumbersAndStrings.ToString(),
-                jsonBuilder_NumbersAsNumbersAndStrings_Alternate.ToString());
-
-            // Reflection based tests for every collection type.
-            RunAllCollectionsRoundTripTest<T>(jsonNumbersAsStrings);
-        }
-
-        private static void PerformAsCollectionElementSerialization<T>(
-            List<T> numbers,
-            string json_NumbersAsNumbers,
-            string json_NumbersAsStrings,
-            string json_NumbersAsNumbersAndStrings,
-            string json_NumbersAsNumbersAndStrings_Alternate)
-        {
-            List<T> deserialized;
-
-            // Option: read from string
-
-            // Deserialize
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbers, s_optionReadFromStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsStrings, s_optionReadFromStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbersAndStrings, s_optionReadFromStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbersAndStrings_Alternate, s_optionReadFromStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            // Serialize
-            Assert.Equal(json_NumbersAsNumbers, JsonSerializer.Serialize(numbers, s_optionReadFromStr));
-
-            // Option: write as string
-
-            // Deserialize
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbers, s_optionWriteAsStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<List<T>>(json_NumbersAsStrings, s_optionWriteAsStr));
-
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbersAndStrings, s_optionWriteAsStr));
-
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbersAndStrings_Alternate, s_optionWriteAsStr));
-
-            // Serialize
-            Assert.Equal(json_NumbersAsStrings, JsonSerializer.Serialize(numbers, s_optionWriteAsStr));
-
-            // Option: read and write from/to string
-
-            // Deserialize
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbers, s_optionReadAndWriteFromStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsStrings, s_optionReadAndWriteFromStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbersAndStrings, s_optionReadAndWriteFromStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            deserialized = JsonSerializer.Deserialize<List<T>>(json_NumbersAsNumbersAndStrings_Alternate, s_optionReadAndWriteFromStr);
-            AssertIEnumerableEqual(numbers, deserialized);
-
-            // Serialize
-            Assert.Equal(json_NumbersAsStrings, JsonSerializer.Serialize(numbers, s_optionReadAndWriteFromStr));
-        }
-
-        private static void AssertIEnumerableEqual<T>(IEnumerable<T> list1, IEnumerable<T> list2)
-        {
-            IEnumerator<T> enumerator1 = list1.GetEnumerator();
-            IEnumerator<T> enumerator2 = list2.GetEnumerator();
-
-            while (enumerator1.MoveNext())
-            {
-                enumerator2.MoveNext();
-                Assert.Equal(enumerator1.Current, enumerator2.Current);
-            }
-
-            Assert.False(enumerator2.MoveNext());
-        }
-
-        private static void RunAllCollectionsRoundTripTest<T>(string json)
-        {
-            foreach (Type type in CollectionTestTypes.DeserializableGenericEnumerableTypes<T>())
-            {
-                if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(HashSet<>))
-                {
-                    HashSet<T> obj1 = (HashSet<T>)JsonSerializer.Deserialize(json, type, s_optionReadAndWriteFromStr);
-                    string serialized = JsonSerializer.Serialize(obj1, s_optionReadAndWriteFromStr);
-
-                    HashSet<T> obj2 = (HashSet<T>)JsonSerializer.Deserialize(serialized, type, s_optionReadAndWriteFromStr);
-
-                    Assert.Equal(obj1.Count, obj2.Count);
-                    foreach (T element in obj1)
-                    {
-                        Assert.True(obj2.Contains(element));
-                    }
-                }
-                else if (type != typeof(byte[]))
-                {
-                    object obj = JsonSerializer.Deserialize(json, type, s_optionReadAndWriteFromStr);
-                    string serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-                    Assert.Equal(json, serialized);
-                }
-            }
-
-            foreach (Type type in CollectionTestTypes.DeserializableNonGenericEnumerableTypes())
-            {
-                // Deserialized as collection of JsonElements.
-                object obj = JsonSerializer.Deserialize(json, type, s_optionReadAndWriteFromStr);
-                // Serialized as strings with escaping.
-                string serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-
-                // Ensure escaped values were serialized accurately
-                List<T> list = JsonSerializer.Deserialize<List<T>>(serialized, s_optionReadAndWriteFromStr);
-                serialized = JsonSerializer.Serialize(list, s_optionReadAndWriteFromStr);
-                Assert.Equal(json, serialized);
-
-                // Serialize instance which is a collection of numbers (not JsonElements).
-                obj = Activator.CreateInstance(type, new[] { list });
-                serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-                Assert.Equal(json, serialized);
-            }
-        }
-
-        [Fact]
-        public static void Number_AsDictionaryElement_RoundTrip()
-        {
-            var dict = new Dictionary<int, float>();
-            for (int i = 0; i < 10; i++)
-            {
-                dict[JsonNumberTestData.Ints[i]] = JsonNumberTestData.Floats[i];
-            }
-
-            // Serialize
-            string serialized = JsonSerializer.Serialize(dict, s_optionReadAndWriteFromStr);
-            AssertDictionaryElements_StringValues(serialized);
-
-            // Deserialize
-            dict = JsonSerializer.Deserialize<Dictionary<int, float>>(serialized, s_optionReadAndWriteFromStr);
-
-            // Test roundtrip
-            JsonTestHelper.AssertJsonEqual(serialized, JsonSerializer.Serialize(dict, s_optionReadAndWriteFromStr));
-        }
-
-        private static void AssertDictionaryElements_StringValues(string serialized)
-        {
-            var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(serialized));
-            reader.Read();
-            while (reader.Read())
-            {
-                if (reader.TokenType == JsonTokenType.EndObject)
-                {
-                    break;
-                }
-                else if (reader.TokenType == JsonTokenType.String)
-                {
-                    Assert.True(reader.ValueSpan.IndexOf((byte)'\\') == -1);
-                }
-                else
-                {
-                    Assert.Equal(JsonTokenType.PropertyName, reader.TokenType);
-                }
-            }
-        }
-
-        [Fact]
-        [ActiveIssue("https://github.com/dotnet/runtime/issues/39674", typeof(PlatformDetection), nameof(PlatformDetection.IsMonoInterpreter))]
-        [SkipOnCoreClr("https://github.com/dotnet/runtime/issues/45464", ~RuntimeConfiguration.Release)]
-        public static void DictionariesRoundTrip()
-        {
-            RunAllDictionariessRoundTripTest(JsonNumberTestData.ULongs);
-            RunAllDictionariessRoundTripTest(JsonNumberTestData.Floats);
-            RunAllDictionariessRoundTripTest(JsonNumberTestData.Doubles);
-        }
-
-        private static void RunAllDictionariessRoundTripTest<T>(List<T> numbers)
-        {
-            StringBuilder jsonBuilder_NumbersAsStrings = new StringBuilder();
-
-            jsonBuilder_NumbersAsStrings.Append("{");
-
-            foreach (T number in numbers)
-            {
-                string numberAsString = GetNumberAsString(number);
-                string jsonWithNumberAsString = @$"""{numberAsString}""";
-
-                jsonBuilder_NumbersAsStrings.Append($"{jsonWithNumberAsString}:");
-                jsonBuilder_NumbersAsStrings.Append($"{jsonWithNumberAsString},");
-            }
-
-            jsonBuilder_NumbersAsStrings.Remove(jsonBuilder_NumbersAsStrings.Length - 1, 1);
-            jsonBuilder_NumbersAsStrings.Append("}");
-
-            string jsonNumbersAsStrings = jsonBuilder_NumbersAsStrings.ToString();
-
-            foreach (Type type in CollectionTestTypes.DeserializableDictionaryTypes<string, T>())
-            {
-                object obj = JsonSerializer.Deserialize(jsonNumbersAsStrings, type, s_optionReadAndWriteFromStr);
-                JsonTestHelper.AssertJsonEqual(jsonNumbersAsStrings, JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr));
-            }
-
-            foreach (Type type in CollectionTestTypes.DeserializableNonGenericDictionaryTypes())
-            {
-                Dictionary<T, T> dict = JsonSerializer.Deserialize<Dictionary<T, T>>(jsonNumbersAsStrings, s_optionReadAndWriteFromStr);
-
-                // Serialize instance which is a dictionary of numbers (not JsonElements).
-                object obj = Activator.CreateInstance(type, new[] { dict });
-                string serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-                JsonTestHelper.AssertJsonEqual(jsonNumbersAsStrings, serialized);
-            }
-        }
-
-        [Fact]
-        public static void Number_AsPropertyValue_RoundTrip()
-        {
-            var obj = new Class_With_NullableUInt64_And_Float()
-            {
-                NullableUInt64Number = JsonNumberTestData.NullableULongs.LastOrDefault(),
-                FloatNumbers = JsonNumberTestData.Floats
-            };
-
-            // Serialize
-            string serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-
-            // Deserialize
-            obj = JsonSerializer.Deserialize<Class_With_NullableUInt64_And_Float>(serialized, s_optionReadAndWriteFromStr);
-
-            // Test roundtrip
-            JsonTestHelper.AssertJsonEqual(serialized, JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr));
-        }
-
-        private class Class_With_NullableUInt64_And_Float
-        {
-            public ulong? NullableUInt64Number { get; set; }
-            [JsonInclude]
-            public List<float> FloatNumbers;
-        }
-
-        [Fact]
-        public static void Number_AsKeyValuePairValue_RoundTrip()
-        {
-            var obj = new KeyValuePair<ulong?, List<float>>(JsonNumberTestData.NullableULongs.LastOrDefault(), JsonNumberTestData.Floats);
-
-            // Serialize
-            string serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-
-            // Deserialize
-            obj = JsonSerializer.Deserialize<KeyValuePair<ulong?, List<float>>>(serialized, s_optionReadAndWriteFromStr);
-
-            // Test roundtrip
-            JsonTestHelper.AssertJsonEqual(serialized, JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr));
-        }
-
-        [Fact]
-        public static void Number_AsObjectWithParameterizedCtor_RoundTrip()
-        {
-            var obj = new MyClassWithNumbers(JsonNumberTestData.NullableULongs.LastOrDefault(), JsonNumberTestData.Floats);
-
-            // Serialize
-            string serialized = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-
-            // Deserialize
-            obj = JsonSerializer.Deserialize<MyClassWithNumbers>(serialized, s_optionReadAndWriteFromStr);
-
-            // Test roundtrip
-            JsonTestHelper.AssertJsonEqual(serialized, JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr));
-        }
-
-        private class MyClassWithNumbers
-        {
-            public ulong? Ulong { get; }
-            public List<float> ListOfFloats { get; }
-
-            public MyClassWithNumbers(ulong? @ulong, List<float> listOfFloats)
-            {
-                Ulong = @ulong;
-                ListOfFloats = listOfFloats;
-            }
-        }
-
-        [Fact]
-        public static void Number_AsObjectWithParameterizedCtor_PropHasAttribute()
-        {
-            string json = @"{""ListOfFloats"":[""1""]}";
-            // Strict handling on property overrides loose global policy.
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<MyClassWithNumbers_PropsHasAttribute>(json, s_optionReadFromStr));
-
-            // Serialize
-            json = @"{""ListOfFloats"":[1]}";
-            MyClassWithNumbers_PropsHasAttribute obj = JsonSerializer.Deserialize<MyClassWithNumbers_PropsHasAttribute>(json);
-
-            // Number serialized as JSON number due to strict handling on property which overrides loose global policy.
-            Assert.Equal(json, JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr));
-        }
-
-        private class MyClassWithNumbers_PropsHasAttribute
-        {
-            [JsonNumberHandling(JsonNumberHandling.Strict)]
-            public List<float> ListOfFloats { get; }
-
-            public MyClassWithNumbers_PropsHasAttribute(List<float> listOfFloats)
-            {
-                ListOfFloats = listOfFloats;
-            }
-        }
-
-        [Fact]
-        public static void FloatingPointConstants_Pass()
-        {
-            // Valid values
-            PerformFloatingPointSerialization("NaN");
-            PerformFloatingPointSerialization("Infinity");
-            PerformFloatingPointSerialization("-Infinity");
-
-            PerformFloatingPointSerialization("\u004EaN"); // NaN
-            PerformFloatingPointSerialization("Inf\u0069ni\u0074y"); // Infinity
-            PerformFloatingPointSerialization("\u002DInf\u0069nity"); // -Infinity
-
-            static void PerformFloatingPointSerialization(string testString)
-            {
-                string testStringAsJson = $@"""{testString}""";
-                string testJson = @$"{{""FloatNumber"":{testStringAsJson},""DoubleNumber"":{testStringAsJson}}}";
-
-                StructWithNumbers obj;
-                switch (testString)
-                {
-                    case "NaN":
-                        obj = JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionsAllowFloatConstants);
-                        Assert.Equal(float.NaN, obj.FloatNumber);
-                        Assert.Equal(double.NaN, obj.DoubleNumber);
-
-                        obj = JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionReadFromStr);
-                        Assert.Equal(float.NaN, obj.FloatNumber);
-                        Assert.Equal(double.NaN, obj.DoubleNumber);
-                        break;
-                    case "Infinity":
-                        obj = JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionsAllowFloatConstants);
-                        Assert.Equal(float.PositiveInfinity, obj.FloatNumber);
-                        Assert.Equal(double.PositiveInfinity, obj.DoubleNumber);
-
-                        obj = JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionReadFromStr);
-                        Assert.Equal(float.PositiveInfinity, obj.FloatNumber);
-                        Assert.Equal(double.PositiveInfinity, obj.DoubleNumber);
-                        break;
-                    case "-Infinity":
-                        obj = JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionsAllowFloatConstants);
-                        Assert.Equal(float.NegativeInfinity, obj.FloatNumber);
-                        Assert.Equal(double.NegativeInfinity, obj.DoubleNumber);
-
-                        obj = JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionReadFromStr);
-                        Assert.Equal(float.NegativeInfinity, obj.FloatNumber);
-                        Assert.Equal(double.NegativeInfinity, obj.DoubleNumber);
-                        break;
-                    default:
-                        Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionsAllowFloatConstants));
-                        return;
-                }
-
-                JsonTestHelper.AssertJsonEqual(testJson, JsonSerializer.Serialize(obj, s_optionsAllowFloatConstants));
-                JsonTestHelper.AssertJsonEqual(testJson, JsonSerializer.Serialize(obj, s_optionWriteAsStr));
-            }
-        }
-
-        [Theory]
-        [InlineData("naN")]
-        [InlineData("Nan")]
-        [InlineData("NAN")]
-        [InlineData("+Infinity")]
-        [InlineData("+infinity")]
-        [InlineData("infinity")]
-        [InlineData("infinitY")]
-        [InlineData("INFINITY")]
-        [InlineData("+INFINITY")]
-        [InlineData("-infinity")]
-        [InlineData("-infinitY")]
-        [InlineData("-INFINITY")]
-        [InlineData(" NaN")]
-        [InlineData("NaN ")]
-        [InlineData(" Infinity")]
-        [InlineData(" -Infinity")]
-        [InlineData("Infinity ")]
-        [InlineData("-Infinity ")]
-        [InlineData("a-Infinity")]
-        [InlineData("NaNa")]
-        [InlineData("Infinitya")]
-        [InlineData("-Infinitya")]
-#pragma warning disable xUnit1025 // Theory method 'FloatingPointConstants_Fail' on test class 'NumberHandlingTests' has InlineData duplicate(s)
-        [InlineData("\u006EaN")] // "naN"
-        [InlineData("\u0020Inf\u0069ni\u0074y")] // " Infinity"
-        [InlineData("\u002BInf\u0069nity")] // "+Infinity"
-#pragma warning restore xUnit1025
-        public static void FloatingPointConstants_Fail(string testString)
-        {
-            string testStringAsJson = $@"""{testString}""";
-
-            string testJson = @$"{{""FloatNumber"":{testStringAsJson}}}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionReadFromStr));
-
-            testJson = @$"{{""DoubleNumber"":{testStringAsJson}}}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<StructWithNumbers>(testJson, s_optionReadFromStr));
-        }
-
-        [Fact]
-        public static void AllowFloatingPointConstants_WriteAsNumber_IfNotConstant()
-        {
-            float @float = 1;
-            // Not written as "1"
-            Assert.Equal("1", JsonSerializer.Serialize(@float, s_optionsAllowFloatConstants));
-
-            double @double = 1;
-            // Not written as "1"
-            Assert.Equal("1", JsonSerializer.Serialize(@double, s_optionsAllowFloatConstants));
-        }
-
-        [Theory]
-        [InlineData("NaN")]
-        [InlineData("Infinity")]
-        [InlineData("-Infinity")]
-        public static void Unquoted_FloatingPointConstants_Read_Fail(string testString)
-        {
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<float>(testString, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<double?>(testString, s_optionReadFromStr));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<double>(testString, s_optionReadFromStrAllowFloatConstants));
-        }
-
-        private struct StructWithNumbers
-        {
-            public float FloatNumber { get; set; }
-            public double DoubleNumber { get; set; }
-        }
-
-        [Fact]
-        public static void ReadFromString_AllowFloatingPoint()
-        {
-            string json = @"{""IntNumber"":""1"",""FloatNumber"":""NaN""}";
-            ClassWithNumbers obj = JsonSerializer.Deserialize<ClassWithNumbers>(json, s_optionReadFromStrAllowFloatConstants);
-
-            Assert.Equal(1, obj.IntNumber);
-            Assert.Equal(float.NaN, obj.FloatNumber);
-
-            JsonTestHelper.AssertJsonEqual(@"{""IntNumber"":1,""FloatNumber"":""NaN""}", JsonSerializer.Serialize(obj, s_optionReadFromStrAllowFloatConstants));
-        }
-
-        [Fact]
-        public static void WriteAsString_AllowFloatingPoint()
-        {
-            string json = @"{""IntNumber"":""1"",""FloatNumber"":""NaN""}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ClassWithNumbers>(json, s_optionWriteAsStrAllowFloatConstants));
-
-            var obj = new ClassWithNumbers
-            {
-                IntNumber = 1,
-                FloatNumber = float.NaN
-            };
-
-            JsonTestHelper.AssertJsonEqual(json, JsonSerializer.Serialize(obj, s_optionWriteAsStrAllowFloatConstants));
-        }
-
-        public class ClassWithNumbers
-        {
-            public int IntNumber { get; set; }
-            public float FloatNumber { get; set; }
-        }
-
-        [Fact]
-        public static void FloatingPointConstants_IncompatibleNumber()
-        {
-            AssertFloatingPointIncompatible_Fails<byte>();
-            AssertFloatingPointIncompatible_Fails<sbyte>();
-            AssertFloatingPointIncompatible_Fails<short>();
-            AssertFloatingPointIncompatible_Fails<int>();
-            AssertFloatingPointIncompatible_Fails<long>();
-            AssertFloatingPointIncompatible_Fails<ushort>();
-            AssertFloatingPointIncompatible_Fails<uint>();
-            AssertFloatingPointIncompatible_Fails<ulong>();
-            AssertFloatingPointIncompatible_Fails<decimal>();
-            AssertFloatingPointIncompatible_Fails<byte?>();
-            AssertFloatingPointIncompatible_Fails<sbyte?>();
-            AssertFloatingPointIncompatible_Fails<short?>();
-            AssertFloatingPointIncompatible_Fails<int?>();
-            AssertFloatingPointIncompatible_Fails<long?>();
-            AssertFloatingPointIncompatible_Fails<ushort?>();
-            AssertFloatingPointIncompatible_Fails<uint?>();
-            AssertFloatingPointIncompatible_Fails<ulong?>();
-            AssertFloatingPointIncompatible_Fails<decimal?>();
-        }
-
-        private static void AssertFloatingPointIncompatible_Fails<T>()
-        {
-            string[] testCases = new[]
-            {
-                @"""NaN""",
-                @"""Infinity""",
-                @"""-Infinity""",
-            };
-
-            foreach (string test in testCases)
-            {
-                Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<T>(test, s_optionReadFromStrAllowFloatConstants));
-            }
-        }
-
-        [Fact]
-        public static void UnsupportedFormats()
-        {
-            AssertUnsupportedFormatThrows<byte>();
-            AssertUnsupportedFormatThrows<sbyte>();
-            AssertUnsupportedFormatThrows<short>();
-            AssertUnsupportedFormatThrows<int>();
-            AssertUnsupportedFormatThrows<long>();
-            AssertUnsupportedFormatThrows<ushort>();
-            AssertUnsupportedFormatThrows<uint>();
-            AssertUnsupportedFormatThrows<ulong>();
-            AssertUnsupportedFormatThrows<float>();
-            AssertUnsupportedFormatThrows<decimal>();
-            AssertUnsupportedFormatThrows<byte?>();
-            AssertUnsupportedFormatThrows<sbyte?>();
-            AssertUnsupportedFormatThrows<short?>();
-            AssertUnsupportedFormatThrows<int?>();
-            AssertUnsupportedFormatThrows<long?>();
-            AssertUnsupportedFormatThrows<ushort?>();
-            AssertUnsupportedFormatThrows<uint?>();
-            AssertUnsupportedFormatThrows<ulong?>();
-            AssertUnsupportedFormatThrows<float?>();
-            AssertUnsupportedFormatThrows<decimal?>();
-        }
-
-        private static void AssertUnsupportedFormatThrows<T>()
-        {
-            string[] testCases = new[]
-            {
-                "$123.46", // Currency
-                "100.00 %", // Percent
-                 "1234,57", // Fixed point
-                 "00FF", // Hexadecimal
-            };
-
-            foreach (string test in testCases)
-            {
-                Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<T>(test, s_optionReadFromStr));
-            }
-        }
-
-        [Fact]
-        public static void EscapingTest()
-        {
-            // Cause all characters to be escaped.
-            var encoderSettings = new TextEncoderSettings();
-            encoderSettings.ForbidCharacters('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '+', '-', 'e', 'E');
-
-            JavaScriptEncoder encoder = JavaScriptEncoder.Create(encoderSettings);
-            var options = new JsonSerializerOptions(s_optionReadAndWriteFromStr)
-            {
-                Encoder = encoder
-            };
-
-            PerformEscapingTest(JsonNumberTestData.Bytes, options);
-            PerformEscapingTest(JsonNumberTestData.SBytes, options);
-            PerformEscapingTest(JsonNumberTestData.Shorts, options);
-            PerformEscapingTest(JsonNumberTestData.Ints, options);
-            PerformEscapingTest(JsonNumberTestData.Longs, options);
-            PerformEscapingTest(JsonNumberTestData.UShorts, options);
-            PerformEscapingTest(JsonNumberTestData.UInts, options);
-            PerformEscapingTest(JsonNumberTestData.ULongs, options);
-            PerformEscapingTest(JsonNumberTestData.Floats, options);
-            PerformEscapingTest(JsonNumberTestData.Doubles, options);
-            PerformEscapingTest(JsonNumberTestData.Decimals, options);
-        }
-
-        private static void PerformEscapingTest<T>(List<T> numbers, JsonSerializerOptions options)
-        {
-            // All input characters are escaped
-            IEnumerable<string> numbersAsStrings = numbers.Select(num => GetNumberAsString(num));
-            string input = JsonSerializer.Serialize(numbersAsStrings, options);
-            AssertListNumbersEscaped(input);
-
-            // Unescaping works
-            List<T> deserialized = JsonSerializer.Deserialize<List<T>>(input, options);
-            Assert.Equal(numbers.Count, deserialized.Count);
-            for (int i = 0; i < numbers.Count; i++)
-            {
-                Assert.Equal(numbers[i], deserialized[i]);
-            }
-
-            // Every number is written as a string, and custom escaping is not honored.
-            string serialized = JsonSerializer.Serialize(deserialized, options);
-            AssertListNumbersUnescaped(serialized);
-        }
-
-        private static void AssertListNumbersEscaped(string json)
-        {
-            var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json));
-            reader.Read();
-            while (reader.Read())
-            {
-                if (reader.TokenType == JsonTokenType.EndArray)
-                {
-                    break;
-                }
-                else
-                {
-                    Assert.Equal(JsonTokenType.String, reader.TokenType);
-                    Assert.True(reader.ValueSpan.IndexOf((byte)'\\') != -1);
-                }
-            }
-        }
-
-        private static void AssertListNumbersUnescaped(string json)
-        {
-            var reader = new Utf8JsonReader(Encoding.UTF8.GetBytes(json));
-            reader.Read();
-            while (reader.Read())
-            {
-                if (reader.TokenType == JsonTokenType.EndArray)
-                {
-                    break;
-                }
-                else
-                {
-                    Assert.Equal(JsonTokenType.String, reader.TokenType);
-                    Assert.True(reader.ValueSpan.IndexOf((byte)'\\') == -1);
-                }
-            }
-        }
-
-        [Fact]
-        public static void Number_RoundtripNull()
-        {
-            Perform_Number_RoundTripNull_Test<byte>();
-            Perform_Number_RoundTripNull_Test<sbyte>();
-            Perform_Number_RoundTripNull_Test<short>();
-            Perform_Number_RoundTripNull_Test<int>();
-            Perform_Number_RoundTripNull_Test<long>();
-            Perform_Number_RoundTripNull_Test<ushort>();
-            Perform_Number_RoundTripNull_Test<uint>();
-            Perform_Number_RoundTripNull_Test<ulong>();
-            Perform_Number_RoundTripNull_Test<float>();
-            Perform_Number_RoundTripNull_Test<decimal>();
-        }
-
-        private static void Perform_Number_RoundTripNull_Test<T>()
-        {
-            string nullAsJson = "null";
-            string nullAsQuotedJson = $@"""{nullAsJson}""";
-
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<T>(nullAsJson, s_optionReadAndWriteFromStr));
-            Assert.Equal("0", JsonSerializer.Serialize(default(T)));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<T>(nullAsQuotedJson, s_optionReadAndWriteFromStr));
-        }
-
-        [Fact]
-        public static void NullableNumber_RoundtripNull()
-        {
-            Perform_NullableNumber_RoundTripNull_Test<byte?>();
-            Perform_NullableNumber_RoundTripNull_Test<sbyte?>();
-            Perform_NullableNumber_RoundTripNull_Test<short?>();
-            Perform_NullableNumber_RoundTripNull_Test<int?>();
-            Perform_NullableNumber_RoundTripNull_Test<long?>();
-            Perform_NullableNumber_RoundTripNull_Test<ushort?>();
-            Perform_NullableNumber_RoundTripNull_Test<uint?>();
-            Perform_NullableNumber_RoundTripNull_Test<ulong?>();
-            Perform_NullableNumber_RoundTripNull_Test<float?>();
-            Perform_NullableNumber_RoundTripNull_Test<decimal?>();
-        }
-
-        private static void Perform_NullableNumber_RoundTripNull_Test<T>()
-        {
-            string nullAsJson = "null";
-            string nullAsQuotedJson = $@"""{nullAsJson}""";
-
-            Assert.Null(JsonSerializer.Deserialize<T>(nullAsJson, s_optionReadAndWriteFromStr));
-            Assert.Equal(nullAsJson, JsonSerializer.Serialize(default(T)));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<T>(nullAsQuotedJson, s_optionReadAndWriteFromStr));
-        }
-
-        [Fact]
-        public static void Disallow_ArbritaryStrings_On_AllowFloatingPointConstants()
-        {
-            string json = @"""12345""";
-
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<byte>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<sbyte>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<short>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<int>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<long>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ushort>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<uint>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ulong>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<float>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<double>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<decimal>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<byte?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<sbyte?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<short?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<int?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<long?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ushort?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<uint?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ulong?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<float?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<double?>(json, s_optionsAllowFloatConstants));
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<decimal?>(json, s_optionsAllowFloatConstants));
-        }
-
-        [Fact]
-        public static void Attributes_OnMembers_Work()
-        {
-            // Bad JSON because Int should not be string.
-            string intIsString = @"{""Float"":""1234.5"",""Int"":""12345""}";
-
-            // Good JSON because Float can be string.
-            string floatIsString = @"{""Float"":""1234.5"",""Int"":12345}";
-
-            // Good JSON because Float can be number.
-            string floatIsNumber = @"{""Float"":1234.5,""Int"":12345}";
-
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ClassWith_Attribute_OnNumber>(intIsString));
-
-            ClassWith_Attribute_OnNumber obj = JsonSerializer.Deserialize<ClassWith_Attribute_OnNumber>(floatIsString);
-            Assert.Equal(1234.5, obj.Float);
-            Assert.Equal(12345, obj.Int);
-
-            obj = JsonSerializer.Deserialize<ClassWith_Attribute_OnNumber>(floatIsNumber);
-            Assert.Equal(1234.5, obj.Float);
-            Assert.Equal(12345, obj.Int);
-
-            // Per options, float should be written as string.
-            JsonTestHelper.AssertJsonEqual(floatIsString, JsonSerializer.Serialize(obj));
-        }
-
-        private class ClassWith_Attribute_OnNumber
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public float Float { get; set; }
-
-            public int Int { get; set; }
-        }
-
-        [Fact]
-        public static void Attribute_OnRootType_Works()
-        {
-            // Not allowed
-            string floatIsString = @"{""Float"":""1234"",""Int"":123}";
-
-            // Allowed
-            string floatIsNan = @"{""Float"":""NaN"",""Int"":123}";
-
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<Type_AllowFloatConstants>(floatIsString));
-
-            Type_AllowFloatConstants obj = JsonSerializer.Deserialize<Type_AllowFloatConstants>(floatIsNan);
-            Assert.Equal(float.NaN, obj.Float);
-            Assert.Equal(123, obj.Int);
-
-            JsonTestHelper.AssertJsonEqual(floatIsNan, JsonSerializer.Serialize(obj));
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.AllowNamedFloatingPointLiterals)]
-        private class Type_AllowFloatConstants
-        {
-            public float Float { get; set; }
-
-            public int Int { get; set; }
-        }
-
-        [Fact]
-        public static void AttributeOnType_WinsOver_GlobalOption()
-        {
-            // Global options strict, type options loose
-            string json = @"{""Float"":""12345""}";
-            var obj1 = JsonSerializer.Deserialize<ClassWith_LooseAttribute>(json);
-
-            Assert.Equal(@"{""Float"":""12345""}", JsonSerializer.Serialize(obj1));
-
-            // Global options loose, type options strict
-            json = @"{""Float"":""12345""}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ClassWith_StrictAttribute>(json, s_optionReadAndWriteFromStr));
-
-            var obj2 = new ClassWith_StrictAttribute() { Float = 12345 };
-            Assert.Equal(@"{""Float"":12345}", JsonSerializer.Serialize(obj2, s_optionReadAndWriteFromStr));
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.Strict)]
-        public class ClassWith_StrictAttribute
-        {
-            public float Float { get; set; }
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-        private class ClassWith_LooseAttribute
-        {
-            public float Float { get; set; }
-        }
-
-        [Fact]
-        public static void AttributeOnMember_WinsOver_AttributeOnType()
-        {
-            string json = @"{""Double"":""NaN""}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ClassWith_Attribute_On_TypeAndMember>(json));
-
-            var obj = new ClassWith_Attribute_On_TypeAndMember { Double = float.NaN };
-            Assert.Throws<ArgumentException>(() => JsonSerializer.Serialize(obj));
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.AllowNamedFloatingPointLiterals)]
-        private class ClassWith_Attribute_On_TypeAndMember
-        {
-            [JsonNumberHandling(JsonNumberHandling.Strict)]
-            public double Double { get; set; }
-        }
-
-        [Fact]
-        public static void Attribute_OnNestedType_Works()
-        {
-            string jsonWithShortProperty = @"{""Short"":""1""}";
-            ClassWith_ReadAsStringAttribute obj = JsonSerializer.Deserialize<ClassWith_ReadAsStringAttribute>(jsonWithShortProperty);
-            Assert.Equal(1, obj.Short);
-
-            string jsonWithMyObjectProperty = @"{""MyObject"":{""Float"":""1""}}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ClassWith_ReadAsStringAttribute>(jsonWithMyObjectProperty));
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
-        public class ClassWith_ReadAsStringAttribute
-        {
-            public short Short { get; set; }
-
-            public ClassWith_StrictAttribute MyObject { get; set; }
-        }
-
-        [Fact]
-        public static void MemberAttributeAppliesToCollection_SimpleElements()
-        {
-            RunTest<int[]>();
-            RunTest<ConcurrentQueue<int>>();
-            RunTest<GenericICollectionWrapper<int>>();
-            RunTest<IEnumerable<int>>();
-            RunTest<Collection<int>>();
-            RunTest<ImmutableList<int>>();
-            RunTest<HashSet<int>>();
-            RunTest<List<int>>();
-            RunTest<IList<int>>();
-            RunTest<IList>();
-            RunTest<Queue<int>>();
-
-            static void RunTest<T>()
-            {
-                string json = @"{""MyList"":[""1"",""2""]}";
-                ClassWithSimpleCollectionProperty<T> obj = global::System.Text.Json.JsonSerializer.Deserialize<ClassWithSimpleCollectionProperty<T>>(json);
-                Assert.Equal(json, global::System.Text.Json.JsonSerializer.Serialize(obj));
-            }
-        }
-
-        public class ClassWithSimpleCollectionProperty<T>
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public T MyList { get; set; }
-        }
-
-        [Fact]
-        public static void NestedCollectionElementTypeHandling_Overrides_GlobalOption()
-        {
-            // Strict policy on the collection element type overrides read-as-string on the collection property
-            string json = @"{""MyList"":[{""Float"":""1""}]}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ClassWithComplexListProperty>(json, s_optionReadAndWriteFromStr));
-
-            // Strict policy on the collection element type overrides write-as-string on the collection property
-            var obj = new ClassWithComplexListProperty
-            {
-                MyList = new List<ClassWith_StrictAttribute> { new ClassWith_StrictAttribute { Float = 1 } }
-            };
-            Assert.Equal(@"{""MyList"":[{""Float"":1}]}", JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr));
-        }
-
-        public class ClassWithComplexListProperty
-        {
-            public List<ClassWith_StrictAttribute> MyList { get; set; }
-        }
-
-        [Fact]
-        public static void NumberHandlingAttribute_NotAllowedOn_CollectionOfNonNumbers()
-        {
-            Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize<ClassWith_AttributeOnComplexListProperty>(""));
-            Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(new ClassWith_AttributeOnComplexListProperty()));
-            Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize<ClassWith_AttributeOnComplexDictionaryProperty>(""));
-            Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(new ClassWith_AttributeOnComplexDictionaryProperty()));
-        }
-
-        public class ClassWith_AttributeOnComplexListProperty
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public List<ClassWith_StrictAttribute> MyList { get; set; }
-        }
-
-        public class ClassWith_AttributeOnComplexDictionaryProperty
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
-            public Dictionary<string, ClassWith_StrictAttribute> MyDictionary { get; set; }
-        }
-
-        [Fact]
-        public static void MemberAttributeAppliesToDictionary_SimpleElements()
-        {
-            string json = @"{""First"":""1"",""Second"":""2""}";
-            ClassWithSimpleDictionaryProperty obj = JsonSerializer.Deserialize<ClassWithSimpleDictionaryProperty>(json);
-        }
-
-        public class ClassWithSimpleDictionaryProperty
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public Dictionary<string, int> MyDictionary { get; set; }
-        }
-
-        [Fact]
-        public static void NestedDictionaryElementTypeHandling_Overrides_GlobalOption()
-        {
-            // Strict policy on the dictionary element type overrides read-as-string on the collection property.
-            string json = @"{""MyDictionary"":{""Key"":{""Float"":""1""}}}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<ClassWithComplexDictionaryProperty>(json, s_optionReadFromStr));
-
-            // Strict policy on the collection element type overrides write-as-string on the collection property
-            var obj = new ClassWithComplexDictionaryProperty
-            {
-                MyDictionary = new Dictionary<string, ClassWith_StrictAttribute> { ["Key"] = new ClassWith_StrictAttribute { Float = 1 } }
-            };
-            Assert.Equal(@"{""MyDictionary"":{""Key"":{""Float"":1}}}", JsonSerializer.Serialize(obj, s_optionReadFromStr));
-        }
-
-        public class ClassWithComplexDictionaryProperty
-        {
-            public Dictionary<string, ClassWith_StrictAttribute> MyDictionary { get; set; }
-        }
-
-        [Fact]
-        public static void TypeAttributeAppliesTo_CustomCollectionElements()
-        {
-            string json = @"[""1""]";
-            MyCustomList obj = JsonSerializer.Deserialize<MyCustomList>(json);
-            Assert.Equal(json, JsonSerializer.Serialize(obj));
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-        public class MyCustomList : List<int> { }
-
-        [Fact]
-        public static void TypeAttributeAppliesTo_CustomCollectionElements_HonoredWhenProperty()
-        {
-            string json = @"{""List"":[""1""]}";
-            ClassWithCustomList obj = JsonSerializer.Deserialize<ClassWithCustomList>(json);
-            Assert.Equal(json, JsonSerializer.Serialize(obj));
-        }
-
-        public class ClassWithCustomList
-        {
-            public MyCustomList List { get; set; }
-        }
-
-        [Fact]
-        public static void TypeAttributeAppliesTo_CustomDictionaryElements()
-        {
-            string json = @"{""Key"":""1""}";
-            MyCustomDictionary obj = JsonSerializer.Deserialize<MyCustomDictionary>(json);
-            Assert.Equal(json, JsonSerializer.Serialize(obj));
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-        public class MyCustomDictionary : Dictionary<string, int> { }
-
-        [Fact]
-        public static void TypeAttributeAppliesTo_CustomDictionaryElements_HonoredWhenProperty()
-        {
-            string json = @"{""Dictionary"":{""Key"":""1""}}";
-            ClassWithCustomDictionary obj = JsonSerializer.Deserialize<ClassWithCustomDictionary>(json);
-            Assert.Equal(json, JsonSerializer.Serialize(obj));
-        }
-
-        public class ClassWithCustomDictionary
-        {
-            public MyCustomDictionary Dictionary { get; set; }
-        }
-
-        [Fact]
-        public static void Attribute_OnType_NotRecursive()
-        {
-            // Recursive behavior, where number handling setting on a property is applied to subsequent
-            // properties in its type closure, would allow a string number. This is not supported.
-            string json = @"{""NestedClass"":{""MyInt"":""1""}}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<AttributeAppliedToFirstLevelProp>(json));
-
-            var obj = new AttributeAppliedToFirstLevelProp
-            {
-                NestedClass = new NonNumberType { MyInt = 1 }
-            };
-            Assert.Equal(@"{""NestedClass"":{""MyInt"":1}}", JsonSerializer.Serialize(obj));
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-        public class AttributeAppliedToFirstLevelProp
-        {
-            public NonNumberType NestedClass { get; set; }
-        }
-
-        public class NonNumberType
-        {
-            public int MyInt { get; set; }
-        }
-
-        [Fact]
-        public static void HandlingOnMemberOverridesHandlingOnType_Enumerable()
-        {
-            string json = @"{""List"":[""1""]}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<MyCustomListWrapper>(json));
-
-            var obj = new MyCustomListWrapper
-            {
-                List = new MyCustomList { 1 }
-            };
-            Assert.Equal(@"{""List"":[1]}", JsonSerializer.Serialize(obj));
-        }
-
-        public class MyCustomListWrapper
-        {
-            [JsonNumberHandling(JsonNumberHandling.Strict)]
-            public MyCustomList List { get; set; }
-        }
-
-        [Fact]
-        public static void HandlingOnMemberOverridesHandlingOnType_Dictionary()
-        {
-            string json = @"{""Dictionary"":{""Key"":""1""}}";
-            Assert.Throws<JsonException>(() => JsonSerializer.Deserialize<MyCustomDictionaryWrapper>(json));
-
-            var obj1 = new MyCustomDictionaryWrapper
-            {
-                Dictionary = new MyCustomDictionary { ["Key"] = 1 }
-            };
-            Assert.Equal(@"{""Dictionary"":{""Key"":1}}", JsonSerializer.Serialize(obj1));
-        }
-
-        public class MyCustomDictionaryWrapper
-        {
-            [JsonNumberHandling(JsonNumberHandling.Strict)]
-            public MyCustomDictionary Dictionary { get; set; }
-        }
-
-        [Fact]
-        public static void Attribute_Allowed_On_NonNumber_NonCollection_Property()
-        {
-            const string Json = @"{""MyProp"":{""MyInt"":1}}";
-
-            ClassWith_NumberHandlingOn_ObjectProperty obj = JsonSerializer.Deserialize<ClassWith_NumberHandlingOn_ObjectProperty>(Json);
-            Assert.Equal(1, obj.MyProp.MyInt);
-
-            string json = JsonSerializer.Serialize(obj);
-            Assert.Equal(Json, json);
-        }
-
-        public class ClassWith_NumberHandlingOn_ObjectProperty
-        {
-            [JsonNumberHandling(JsonNumberHandling.Strict)]
-            public NonNumberType MyProp { get; set; }
-        }
-
-        [Fact]
-        public static void Attribute_Allowed_On_Property_WithCustomConverter()
-        {
-            string json = @"{""Prop"":1}";
-
-            // Converter returns 25 regardless of input.
-            var obj = JsonSerializer.Deserialize<ClassWith_NumberHandlingOn_Property_WithCustomConverter>(json);
-            Assert.Equal(25, obj.Prop);
-
-            // Converter throws this exception regardless of input.
-            NotImplementedException ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(obj));
-            Assert.Equal("Converter was called", ex.Message);
-        }
-
-        public class ClassWith_NumberHandlingOn_Property_WithCustomConverter
-        {
-            [JsonNumberHandling(JsonNumberHandling.Strict)]
-            [JsonConverter(typeof(ConverterForInt32))]
-            public int Prop { get; set; }
-        }
-
-        [Fact]
-        public static void Attribute_Allowed_On_Type_WithCustomConverter()
-        {
-            string json = @"{}";
-            NotImplementedException ex;
-
-            // Assert regular Read/Write methods on custom converter are called.
-            ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Deserialize<ClassWith_NumberHandlingOn_Type_WithCustomConverter>(json));
-            Assert.Equal("Converter was called", ex.Message);
-
-            ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(new ClassWith_NumberHandlingOn_Type_WithCustomConverter()));
-            Assert.Equal("Converter was called", ex.Message);
-        }
-
-        [JsonNumberHandling(JsonNumberHandling.Strict)]
-        [JsonConverter(typeof(ConverterForMyType))]
-        public class ClassWith_NumberHandlingOn_Type_WithCustomConverter
-        {
-        }
-
-        private class ConverterForMyType : JsonConverter<ClassWith_NumberHandlingOn_Type_WithCustomConverter>
-        {
-            public override ClassWith_NumberHandlingOn_Type_WithCustomConverter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
-            {
-                throw new NotImplementedException("Converter was called");
-            }
-
-            public override void Write(Utf8JsonWriter writer, ClassWith_NumberHandlingOn_Type_WithCustomConverter value, JsonSerializerOptions options)
-            {
-                throw new NotImplementedException("Converter was called");
-            }
-        }
-
-        [Fact]
-        public static void CustomConverterOverridesBuiltInLogic()
-        {
-            var options = new JsonSerializerOptions(s_optionReadAndWriteFromStr)
-            {
-                Converters = { new ConverterForInt32(), new ConverterForFloat() }
-            };
-
-            string json = @"""32""";
-
-            // Converter returns 25 regardless of input.
-            Assert.Equal(25, JsonSerializer.Deserialize<int>(json, options));
-
-            // Converter throws this exception regardless of input.
-            NotImplementedException ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(4, options));
-            Assert.Equal("Converter was called", ex.Message);
-
-            json = @"""NaN""";
-
-            // Converter returns 25 if NaN.
-            Assert.Equal(25, JsonSerializer.Deserialize<float?>(json, options));
-
-            // Converter writes 25 if NaN.
-            Assert.Equal("25", JsonSerializer.Serialize((float?)float.NaN, options));
-        }
-
-        public class ConverterForFloat : JsonConverter<float?>
-        {
-            public override float? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
-            {
-                if (reader.TokenType == JsonTokenType.String && reader.GetString() == "NaN")
-                {
-                    return 25;
-                }
-
-                throw new NotSupportedException();
-            }
-
-            public override void Write(Utf8JsonWriter writer, float? value, JsonSerializerOptions options)
-            {
-                if (float.IsNaN(value.Value))
-                {
-                    writer.WriteNumberValue(25);
-                    return;
-                }
-
-                throw new NotSupportedException();
-            }
-        }
-
-        [Fact]
-        public static void JsonNumberHandling_ArgOutOfRangeFail()
-        {
-            // Global options
-            ArgumentOutOfRangeException ex = Assert.Throws<ArgumentOutOfRangeException>(
-                () => new JsonSerializerOptions { NumberHandling = (JsonNumberHandling)(-1) });
-            Assert.Contains("value", ex.ToString());
-            Assert.Throws<ArgumentOutOfRangeException>(
-                () => new JsonSerializerOptions { NumberHandling = (JsonNumberHandling)(8) });
-
-            ex = Assert.Throws<ArgumentOutOfRangeException>(
-                () => new JsonNumberHandlingAttribute((JsonNumberHandling)(-1)));
-            Assert.Contains("handling", ex.ToString());
-            Assert.Throws<ArgumentOutOfRangeException>(
-                () => new JsonNumberHandlingAttribute((JsonNumberHandling)(8)));
-        }
-
-        [Fact]
-        public static void InternalCollectionConverter_CustomNumberConverter_GlobalOption()
-        {
-            NotImplementedException ex;
-
-            var list = new List<int> { 1 };
-            var options = new JsonSerializerOptions(s_optionReadAndWriteFromStr)
-            {
-                Converters = { new ConverterForInt32() }
-            };
-
-            // Assert converter methods are called and not Read/WriteWithNumberHandling (which would throw InvalidOperationException).
-            // Converter returns 25 regardless of input.
-            Assert.Equal(25, JsonSerializer.Deserialize<List<int>>(@"[""1""]", options)[0]);
-            // Converter throws this exception regardless of input.
-            ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(list, options));
-            Assert.Equal("Converter was called", ex.Message);
-
-            var list2 = new List<int?> { 1 };
-            Assert.Equal(25, JsonSerializer.Deserialize<List<int?>>(@"[""1""]", options)[0]);
-            ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(list2, options));
-            Assert.Equal("Converter was called", ex.Message);
-
-            // Okay to set number handling for number collection property when number is handled with custom converter;
-            // converter Read/Write methods called.
-            ClassWithListPropAndAttribute obj1 = JsonSerializer.Deserialize<ClassWithListPropAndAttribute>(@"{""Prop"":[""1""]}", options);
-            Assert.Equal(25, obj1.Prop[0]);
-            ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(obj1, options));
-            Assert.Equal("Converter was called", ex.Message);
-
-            ClassWithDictPropAndAttribute obj2 = JsonSerializer.Deserialize<ClassWithDictPropAndAttribute>(@"{""Prop"":{""1"":""1""}}", options);
-            Assert.Equal(25, obj2.Prop[1]);
-            ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(obj2, options));
-            Assert.Equal("Converter was called", ex.Message);
-        }
-
-        private class ClassWithListPropAndAttribute
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public List<int> Prop { get; set; }
-        }
-
-        private class ClassWithDictPropAndAttribute
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            public Dictionary<int, int?> Prop { get; set; }
-        }
-
-        [Fact]
-        public static void InternalCollectionConverter_CustomNumberConverter_OnProperty()
-        {
-            // Invalid to set number handling for number collection property when number is handled with custom converter.
-            var ex = Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize<ClassWithListPropAndAttribute_ConverterOnProp>(""));
-            Assert.Contains(nameof(ClassWithListPropAndAttribute_ConverterOnProp), ex.ToString());
-            Assert.Contains("IntProp", ex.ToString());
-
-            ex = Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(new ClassWithListPropAndAttribute_ConverterOnProp()));
-            Assert.Contains(nameof(ClassWithListPropAndAttribute_ConverterOnProp), ex.ToString());
-            Assert.Contains("IntProp", ex.ToString());
-
-            ex = Assert.Throws<InvalidOperationException>(() => JsonSerializer.Deserialize<ClassWithDictPropAndAttribute_ConverterOnProp>(""));
-            Assert.Contains(nameof(ClassWithDictPropAndAttribute_ConverterOnProp), ex.ToString());
-            Assert.Contains("IntProp", ex.ToString());
-
-            ex = Assert.Throws<InvalidOperationException>(() => JsonSerializer.Serialize(new ClassWithDictPropAndAttribute_ConverterOnProp()));
-            Assert.Contains(nameof(ClassWithDictPropAndAttribute_ConverterOnProp), ex.ToString());
-            Assert.Contains("IntProp", ex.ToString());
-        }
-
-        private class ClassWithListPropAndAttribute_ConverterOnProp
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            [JsonConverter(typeof(ListOfIntConverter))]
-            public List<int> IntProp { get; set; }
-        }
-
-        private class ClassWithDictPropAndAttribute_ConverterOnProp
-        {
-            [JsonNumberHandling(JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString)]
-            [JsonConverter(typeof(ClassWithDictPropAndAttribute_ConverterOnProp))]
-            public Dictionary<int, int?> IntProp { get; set; }
-        }
-
-        public class ListOfIntConverter : JsonConverter<List<int>>
-        {
-            public override List<int> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException();
-            public override void Write(Utf8JsonWriter writer, List<int> value, JsonSerializerOptions options) => throw new NotImplementedException();
-        }
-
-        [Fact]
-        public static void InternalCollectionConverter_CustomNullableNumberConverter()
-        {
-            NotImplementedException ex;
-
-            var dict = new Dictionary<int, int?> { [1] = 1 };
-            var options = new JsonSerializerOptions(s_optionReadAndWriteFromStr)
-            {
-                Converters = { new ConverterForNullableInt32() }
-            };
-
-            // Assert converter methods are called and not Read/WriteWithNumberHandling (which would throw InvalidOperationException).
-            // Converter returns 25 regardless of input.
-            Assert.Equal(25, JsonSerializer.Deserialize<Dictionary<int, int?>>(@"{""1"":""1""}", options)[1]);
-            ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(dict, options));
-            Assert.Equal("Converter was called", ex.Message);
-
-            var obj = JsonSerializer.Deserialize<ClassWithDictPropAndAttribute>(@"{""Prop"":{""1"":""1""}}", options);
-            Assert.Equal(25, obj.Prop[1]);
-            ex = Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(obj, options));
-            Assert.Throws<NotImplementedException>(() => JsonSerializer.Serialize(dict, options));
-            Assert.Equal("Converter was called", ex.Message);
-        }
-
-        public class ConverterForNullableInt32 : JsonConverter<int?>
-        {
-            public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
-            {
-                return 25;
-            }
-
-            public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options)
-            {
-                throw new NotImplementedException("Converter was called");
-            }
-        }
-
-        /// <summary>
-        /// Example of a custom converter that uses the options to determine behavior.
-        /// </summary>
-        [Fact]
-        public static void AdaptableCustomConverter()
-        {
-            // Baseline without custom converter
-            PlainClassWithList obj = new() { Prop = new List<int>() { 1 } };
-            string json = JsonSerializer.Serialize(obj, s_optionReadAndWriteFromStr);
-            Assert.Equal("{\"Prop\":[\"1\"]}", json);
-
-            obj = JsonSerializer.Deserialize<PlainClassWithList>(json, s_optionReadAndWriteFromStr);
-            Assert.Equal(1, obj.Prop[0]);
-
-            // First with numbers
-            JsonSerializerOptions options = new()
-            {
-                Converters = { new AdaptableInt32Converter() }
-            };
-
-            obj = new PlainClassWithList() { Prop = new List<int>() { 1 } };
-            json = JsonSerializer.Serialize(obj, options);
-            Assert.Equal("{\"Prop\":[101]}", json);
-
-            obj = JsonSerializer.Deserialize<PlainClassWithList>(json, options);
-            Assert.Equal(1, obj.Prop[0]);
-
-            // Then with strings
-            options = new JsonSerializerOptions()
-            {
-                NumberHandling = JsonNumberHandling.AllowReadingFromString | JsonNumberHandling.WriteAsString,
-                Converters = { new AdaptableInt32Converter() }
-            };
-
-            obj = new PlainClassWithList() { Prop = new List<int>() { 1 } };
-            json = JsonSerializer.Serialize(obj, options);
-            Assert.Equal("{\"Prop\":[\"101\"]}", json);
-
-            obj = JsonSerializer.Deserialize<PlainClassWithList>(json, options);
-            Assert.Equal(1, obj.Prop[0]);
-        }
-
-        private class PlainClassWithList
-        {
-            public List<int> Prop { get; set; }
-        }
-
-        public class AdaptableInt32Converter : JsonConverter<int>
-        {
-            public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
-            {
-                if ((JsonNumberHandling.AllowReadingFromString & options.NumberHandling) != 0)
-                {
-                    // Assume it's a string; don't use TryParse().
-                    return int.Parse(reader.GetString(), CultureInfo.InvariantCulture) - 100;
-                }
-                else
-                {
-                    return reader.GetInt32() - 100;
-                }
-            }
-
-            public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
-            {
-                if ((JsonNumberHandling.WriteAsString & options.NumberHandling) != 0)
-                {
-                    writer.WriteStringValue((value + 100).ToString(CultureInfo.InvariantCulture));
-                }
-                else
-                {
-                    writer.WriteNumberValue(value + 100);
-                }
-            }
-        }
+        public NumberHandlingTestsDynamic() : base(JsonSerializerWrapper.StringSerializer) { }
     }
 
     public class NumberHandlingTests_AsyncStreamOverload : NumberHandlingTests_OverloadSpecific
@@ -1877,7 +62,7 @@ namespace System.Text.Json.Serialization.Tests
             };
 
             Result result = await Serializer.DeserializeWrapper<Result>(json, options);
-            JsonTestHelper.AssertJsonEqual(json, JsonSerializer.Serialize(result, options));
+            JsonTestHelper.AssertJsonEqual(json, await Serializer.SerializeWrapper(result, options));
         }
 
         public static IEnumerable<object[]> NumberHandling_ForPropsReadAfter_DeserializingCtorParams_TestData()
index d34d3d1..676a0bd 100644 (file)
     <Compile Include="..\Common\JsonCreationHandlingTests.Enumerable.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\JsonCreationHandlingTests.Enumerable.cs" />
     <Compile Include="..\Common\JsonCreationHandlingTests.Generic.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\JsonCreationHandlingTests.Generic.cs" />
     <Compile Include="..\Common\JsonCreationHandlingTests.Object.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\JsonCreationHandlingTests.Object.cs" />
+    <Compile Include="..\Common\JsonNumberTestData.cs" Link="CommonTest\System\Text\Json\Tests\JsonNumberTestData" />
     <Compile Include="..\Common\JsonSerializerWrapper.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\JsonSerializerWrapper.cs" />
     <Compile Include="..\Common\JsonTestHelper.cs" Link="CommonTest\System\Text\Json\JsonTestHelper.cs" />
     <Compile Include="..\Common\NodeInteropTests.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\NodeInteropTests.cs" />
+    <Compile Include="..\Common\NumberHandlingTests.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\NumberHandlingTests.cs" />
     <Compile Include="..\Common\PropertyNameTests.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\PropertyNameTests.cs" />
     <Compile Include="..\Common\PropertyVisibilityTests.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\PropertyVisibilityTests.cs" />
     <Compile Include="..\Common\PropertyVisibilityTests.InitOnly.cs" Link="CommonTest\System\Text\Json\Tests\Serialization\PropertyVisibilityTests.InitOnly.cs" />
     <Compile Include="JsonNode\ParseTests.cs" />
     <Compile Include="JsonNode\ParentPathRootTests.cs" />
     <Compile Include="JsonNode\ToStringTests.cs" />
-    <Compile Include="JsonNumberTestData.cs" />
     <Compile Include="JsonPropertyTests.cs" />
     <Compile Include="JsonReaderStateAndOptionsTests.cs" />
     <Compile Include="JsonTestHelper.cs" />