Do not surface resolver exceptions when running ancestor type resolution (#85108)
authorEirik Tsarpalis <eirik.tsarpalis@gmail.com>
Thu, 20 Apr 2023 17:18:02 +0000 (18:18 +0100)
committerGitHub <noreply@github.com>
Thu, 20 Apr 2023 17:18:02 +0000 (18:18 +0100)
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/PolymorphicTypeResolver.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/PolymorphicTests.cs

index f0bb547..f2f1f5a 100644 (file)
@@ -639,6 +639,7 @@ namespace System.Text.Json.Serialization.Metadata
             get
             {
                 Debug.Assert(IsConfigured);
+                Debug.Assert(Type != typeof(object));
 
                 if (!_isAncestorPolymorphicTypeResolved)
                 {
index e81103b..b1bd554 100644 (file)
@@ -252,7 +252,7 @@ namespace System.Text.Json.Serialization.Metadata
             // First, walk up the class hierarchy for any supported types.
             for (Type? candidate = typeInfo.Type.BaseType; candidate != null; candidate = candidate.BaseType)
             {
-                JsonTypeInfo? candidateInfo = typeInfo.Options.GetTypeInfoInternal(candidate, ensureNotNull: null);
+                JsonTypeInfo? candidateInfo = ResolveAncestorTypeInfo(candidate, typeInfo.Options);
                 if (candidateInfo?.PolymorphismOptions != null)
                 {
                     // stop on the first ancestor that has a match
@@ -264,7 +264,7 @@ namespace System.Text.Json.Serialization.Metadata
             // Now, walk the interface hierarchy for any polymorphic interface declarations.
             foreach (Type interfaceType in typeInfo.Type.GetInterfaces())
             {
-                JsonTypeInfo? candidateInfo = typeInfo.Options.GetTypeInfoInternal(interfaceType, ensureNotNull: null);
+                JsonTypeInfo? candidateInfo = ResolveAncestorTypeInfo(interfaceType, typeInfo.Options);
                 if (candidateInfo?.PolymorphismOptions != null)
                 {
                     if (matchingResult != null)
@@ -294,6 +294,20 @@ namespace System.Text.Json.Serialization.Metadata
             }
 
             return matchingResult;
+
+            static JsonTypeInfo? ResolveAncestorTypeInfo(Type type, JsonSerializerOptions options)
+            {
+                try
+                {
+                    return options.GetTypeInfoInternal(type, ensureNotNull: null);
+                }
+                catch
+                {
+                    // The resolver produced an exception when resolving the ancestor type.
+                    // Eat the exception and report no result instead.
+                    return null;
+                }
+            }
         }
 
         /// <summary>
index 56158ea..46f2202 100644 (file)
@@ -519,6 +519,36 @@ namespace System.Text.Json.Serialization.Tests
             Assert.Equal(Expected, json);
         }
 
+        [Fact]
+        public async Task CustomResolverWithFailingAncestorType_DoesNotSurfaceException()
+        {
+            var options = new JsonSerializerOptions
+            {
+                TypeInfoResolver = new DefaultJsonTypeInfoResolver
+                {
+                    Modifiers =
+                    {
+                        static typeInfo =>
+                        {
+                            if (typeInfo.Type == typeof(MyThing) ||
+                                typeInfo.Type == typeof(IList))
+                            {
+                                throw new InvalidOperationException("some latent custom resolution bug");
+                            }
+                        }
+                    }
+                }
+            };
+
+            object value = new MyDerivedThing { Number = 42 };
+            string json = await Serializer.SerializeWrapper(value, options);
+            Assert.Equal("""{"Number":42}""", json);
+
+            value = new int[] { 1, 2, 3 };
+            json = await Serializer.SerializeWrapper(value, options);
+            Assert.Equal("[1,2,3]", json);
+        }
+
         class MyClass
         {
             public string Value { get; set; }
@@ -535,6 +565,10 @@ namespace System.Text.Json.Serialization.Tests
             public int Number { get; set; }
         }
 
+        class MyDerivedThing : MyThing
+        {
+        }
+
         class MyThingCollection : List<IThing> { }
 
         class MyThingDictionary : Dictionary<string, IThing> { }