diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs index 055c94c8334b9b0c102374df1046b1107d2ad793..1f60afd061d2ddc14c90812f36f1fc65a3145469 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs @@ -412,6 +412,11 @@ internal string GetDebugInfo() [RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use generic overload or System.Text.Json source generation for native AOT applications.")] public static JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions options) { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + return new CustomJsonTypeInfo(options); } @@ -427,6 +432,21 @@ public static JsonTypeInfo CreateJsonTypeInfo(JsonSerializerOptions option [RequiresDynamicCode("JSON serialization and deserialization might require types that cannot be statically analyzed and might need runtime code generation. Use generic overload or System.Text.Json source generation for native AOT applications.")] public static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions options) { + if (type == null) + { + throw new ArgumentNullException(nameof(type)); + } + + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (IsInvalidForSerialization(type)) + { + ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(type), type, null, null); + } + s_createJsonTypeInfo ??= typeof(JsonTypeInfo).GetMethod(nameof(CreateJsonTypeInfo), new Type[] { typeof(JsonSerializerOptions) })!; return (JsonTypeInfo)s_createJsonTypeInfo.MakeGenericMethod(type) .Invoke(null, new object[] { options })!; @@ -440,7 +460,20 @@ public static JsonTypeInfo CreateJsonTypeInfo(Type type, JsonSerializerOptions o /// JsonPropertyInfo instance public JsonPropertyInfo CreateJsonPropertyInfo(Type propertyType, string name) { - ValidateType(propertyType, Type, null, Options); + if (propertyType == null) + { + throw new ArgumentNullException(nameof(propertyType)); + } + + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + + if (IsInvalidForSerialization(propertyType)) + { + ThrowHelper.ThrowArgumentException_CannotSerializeInvalidType(nameof(propertyType), propertyType, Type, name); + } JsonConverter converter = GetConverter(propertyType, parentClassType: null, @@ -609,7 +642,7 @@ internal static void ValidateType(Type type, Type? parentClassType, MemberInfo? internal static bool IsInvalidForSerialization(Type type) { - return type.IsPointer || type.IsByRef || IsByRefLike(type) || type.ContainsGenericParameters; + return type == typeof(void) || type.IsPointer || type.IsByRef || IsByRefLike(type) || type.ContainsGenericParameters; } private static bool IsByRefLike(Type type) diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs index 0f08484bdc69a2de3069b040fd6e6b7baa4521ba..460bc0cc02afc9c8828cf72794b1bd5fc4003418 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/ThrowHelper.Serialization.cs @@ -85,6 +85,19 @@ public static void ThrowJsonException(string? message = null) throw new JsonException(message) { AppendPathInformation = true }; } + [DoesNotReturn] + public static void ThrowArgumentException_CannotSerializeInvalidType(string paramName, Type type, Type? parentClassType, string? propertyName) + { + if (parentClassType == null) + { + Debug.Assert(propertyName == null); + throw new ArgumentException(SR.Format(SR.CannotSerializeInvalidType, type), paramName); + } + + Debug.Assert(propertyName != null); + throw new ArgumentException(SR.Format(SR.CannotSerializeInvalidMember, type, propertyName, parentClassType), paramName); + } + [DoesNotReturn] public static void ThrowInvalidOperationException_CannotSerializeInvalidType(Type type, Type? parentClassType, MemberInfo? memberInfo) { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonTypeInfo.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonTypeInfo.cs index d46978ee314fb2376f8155db33c91ee8296aee84..62fba16e678b72ce6886649547873e0cc4b1d145 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonTypeInfo.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.JsonTypeInfo.cs @@ -580,6 +580,54 @@ public static void AddingNullJsonPropertyInfoIsNotPossible() Assert.NotNull(typeInfo.Properties[0]); } + [Fact] + public static void CreateJsonTypeInfoWithNullArgumentsThrows() + { + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(null, new JsonSerializerOptions())); + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(typeof(string), null)); + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(null, null)); + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(null)); + } + + [Theory] + [InlineData(typeof(void))] + [InlineData(typeof(Dictionary<,>))] + [InlineData(typeof(List<>))] + [InlineData(typeof(Nullable<>))] + [InlineData(typeof(int*))] + [InlineData(typeof(RefStruct))] + public static void CreateJsonTypeInfoWithInappropriateTypeThrows(Type type) + { + Assert.Throws(() => JsonTypeInfo.CreateJsonTypeInfo(type, new JsonSerializerOptions())); + } + + ref struct RefStruct + { + public int Foo { get; set; } + } + + [Fact] + public static void CreateJsonPropertyInfoWithNullArgumentsThrows() + { + JsonTypeInfo ti = JsonTypeInfo.CreateJsonTypeInfo(new JsonSerializerOptions()); + Assert.Throws(() => ti.CreateJsonPropertyInfo(null, "test")); + Assert.Throws(() => ti.CreateJsonPropertyInfo(typeof(string), null)); + Assert.Throws(() => ti.CreateJsonPropertyInfo(null, null)); + } + + [Theory] + [InlineData(typeof(void))] + [InlineData(typeof(Dictionary<,>))] + [InlineData(typeof(List<>))] + [InlineData(typeof(Nullable<>))] + [InlineData(typeof(int*))] + [InlineData(typeof(RefStruct))] + public static void CreateJsonPropertyInfoWithInappropriateTypeThrows(Type type) + { + JsonTypeInfo ti = JsonTypeInfo.CreateJsonTypeInfo(new JsonSerializerOptions()); + Assert.Throws(() => ti.CreateJsonPropertyInfo(type, "test")); + } + [Theory] [InlineData(typeof(object))] [InlineData(typeof(string))] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs index a1b1b3d2cc03898ae511f2fa61111718c96a626c..c9264f32535dd7baf48610714efed60d5c471d08 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/DefaultJsonTypeInfoResolverTests.cs @@ -178,6 +178,14 @@ public static void ModifiersAreCalledAndModifyTypeInfos() Assert.True(secondModifierCalled); } + [Fact] + public static void AddingNullModifierThrows() + { + DefaultJsonTypeInfoResolver r = new(); + Assert.Throws(() => r.Modifiers.Add(null)); + Assert.Throws(() => r.Modifiers.Insert(0, null)); + } + private static void InvokeGeneric(Type type, string methodName, params object[] args) { typeof(DefaultJsonTypeInfoResolverTests)