Move System.Object serialization to ObjectConverter (#54436)
authorEirik Tsarpalis <eirik.tsarpalis@gmail.com>
Wed, 23 Jun 2021 21:37:17 +0000 (00:37 +0300)
committerGitHub <noreply@github.com>
Wed, 23 Jun 2021 21:37:17 +0000 (21:37 +0000)
* move System.Object serialization to ObjectConverter

* simply customized object converter test

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs
src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonPropertyInfoOfT.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs
src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs

index 597dce6..65b47d6 100644 (file)
@@ -24,7 +24,10 @@ namespace System.Text.Json.Serialization.Converters
 
         public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
         {
-            throw new InvalidOperationException();
+            Debug.Assert(value?.GetType() == typeof(object));
+
+            writer.WriteStartObject();
+            writer.WriteEndObject();
         }
 
         internal override object ReadWithQuotes(ref Utf8JsonReader reader)
index 543fbe2..33f45fd 100644 (file)
@@ -18,12 +18,11 @@ namespace System.Text.Json.Serialization
         /// </summary>
         protected internal JsonConverter()
         {
-            // Today only typeof(object) can have polymorphic writes.
-            // In the future, this will be check for !IsSealed (and excluding value types).
-            CanBePolymorphic = TypeToConvert == JsonTypeInfo.ObjectType;
+            IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly;
+            // Today only the internal JsonConverter<object> can have polymorphic writes.
+            CanBePolymorphic = IsInternalConverter && TypeToConvert == JsonTypeInfo.ObjectType;
             IsValueType = TypeToConvert.IsValueType;
             CanBeNull = default(T) is null;
-            IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly;
 
             if (HandleNull)
             {
@@ -332,7 +331,7 @@ namespace System.Text.Json.Serialization
             bool ignoreCyclesPopReference = false;
 
             if (
-#if NET6_0_OR_GREATER
+#if NET5_0_OR_GREATER
                 !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
 #else
                 !IsValueType &&
@@ -368,15 +367,11 @@ namespace System.Text.Json.Serialization
 
                 if (CanBePolymorphic)
                 {
+                    Debug.Assert(IsInternalConverter);
+
                     Type type = value.GetType();
-                    if (type == JsonTypeInfo.ObjectType)
-                    {
-                        writer.WriteStartObject();
-                        writer.WriteEndObject();
-                        return true;
-                    }
 
-                    if (type != TypeToConvert && IsInternalConverter)
+                    if (type != TypeToConvert)
                     {
                         // For internal converter only: Handle polymorphic case and get the new converter.
                         // Custom converter, even though polymorphic converter, get called for reading AND writing.
@@ -420,6 +415,19 @@ namespace System.Text.Json.Serialization
                 }
 
                 VerifyWrite(originalPropertyDepth, writer);
+
+                if (
+#if NET5_0_OR_GREATER
+                    !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
+#endif
+                    ignoreCyclesPopReference)
+                {
+                    // should only be entered if we're serializing instances
+                    // of type object using the internal object converter.
+                    Debug.Assert(value?.GetType() == typeof(object) && IsInternalConverter);
+                    state.ReferenceResolver.PopReferenceForCycleDetection();
+                }
+
                 return true;
             }
 
index 0f2ff85..8e268c2 100644 (file)
@@ -235,7 +235,7 @@ namespace System.Text.Json.Serialization.Metadata
             T value = Get!(obj);
 
             if (
-#if NET6_0_OR_GREATER
+#if NET5_0_OR_GREATER
                 !typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
 #else
                 !Converter.IsValueType &&
index 0ab7ad0..3630961 100644 (file)
@@ -307,7 +307,9 @@ namespace System.Text.Json.Serialization.Tests
 
             public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
             {
-                throw new InvalidOperationException("Should not get here.");
+                Assert.IsType<object>(value);
+                writer.WriteStartObject();
+                writer.WriteEndObject();
             }
         }
 
@@ -732,5 +734,23 @@ namespace System.Text.Json.Serialization.Tests
             options.Converters.Add(new SystemObjectNewtonsoftCompatibleConverter());
             Verify(options);
         }
+
+        [Fact]
+        public static void CanCustomizeSystemObjectSerialization()
+        {
+            var options = new JsonSerializerOptions { Converters = { new CustomSystemObjectConverter() } };
+
+            string expectedJson = "42";
+            string actualJson = JsonSerializer.Serialize(new object(), options);
+            Assert.Equal(expectedJson, actualJson);
+        }
+
+        private class CustomSystemObjectConverter : JsonConverter<object>
+        {
+            public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException();
+            public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
+                => writer.WriteNumberValue(42);
+        }
     }
+
 }
index 06747c8..f7d80c2 100644 (file)
@@ -328,12 +328,12 @@ namespace System.Text.Json.Serialization.Tests
         [Fact]
         public static void Options_GetConverterForObjectJsonElement_GivesCorrectConverter()
         {
-            GenericObjectOrJsonElementConverterTestHelper<object>("ObjectConverter", new object(), "[3]", true);
+            GenericObjectOrJsonElementConverterTestHelper<object>("ObjectConverter", new object(), "{}");
             JsonElement element = JsonDocument.Parse("[3]").RootElement;
-            GenericObjectOrJsonElementConverterTestHelper<JsonElement>("JsonElementConverter", element, "[3]", false);
+            GenericObjectOrJsonElementConverterTestHelper<JsonElement>("JsonElementConverter", element, "[3]");
         }
 
-        private static void GenericObjectOrJsonElementConverterTestHelper<T>(string converterName, object objectValue, string stringValue, bool throws)
+        private static void GenericObjectOrJsonElementConverterTestHelper<T>(string converterName, object objectValue, string stringValue)
         {
             var options = new JsonSerializerOptions();
 
@@ -347,10 +347,7 @@ namespace System.Text.Json.Serialization.Tests
 
             if (readValue is JsonElement element)
             {
-                Assert.Equal(JsonValueKind.Array, element.ValueKind);
-                JsonElement.ArrayEnumerator iterator = element.EnumerateArray();
-                Assert.True(iterator.MoveNext());
-                Assert.Equal(3, iterator.Current.GetInt32());
+                JsonTestHelper.AssertJsonEqual(stringValue, element.ToString());
             }
             else
             {
@@ -360,22 +357,14 @@ namespace System.Text.Json.Serialization.Tests
             using (var stream = new MemoryStream())
             using (var writer = new Utf8JsonWriter(stream))
             {
-                if (throws)
-                {
-                    Assert.Throws<InvalidOperationException>(() => converter.Write(writer, (T)objectValue, options));
-                    Assert.Throws<InvalidOperationException>(() => converter.Write(writer, (T)objectValue, null));
-                }
-                else
-                {
-                    converter.Write(writer, (T)objectValue, options);
-                    writer.Flush();
-                    Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray()));
-
-                    writer.Reset(stream);
-                    converter.Write(writer, (T)objectValue, null); // Test with null option
-                    writer.Flush();
-                    Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray()));
-                }
+                converter.Write(writer, (T)objectValue, options);
+                writer.Flush();
+                Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray()));
+
+                writer.Reset(stream);
+                converter.Write(writer, (T)objectValue, null); // Test with null option
+                writer.Flush();
+                Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray()));
             }
         }