Skip to content

Commit b14bcdf

Browse files
move System.Object serialization to ObjectConverter
1 parent b1a9074 commit b14bcdf

File tree

5 files changed

+63
-44
lines changed

5 files changed

+63
-44
lines changed

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Value/ObjectConverter.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@ public ObjectConverter()
2424

2525
public override void Write(Utf8JsonWriter writer, object? value, JsonSerializerOptions options)
2626
{
27-
throw new InvalidOperationException();
27+
Debug.Assert(value?.GetType() == typeof(object));
28+
29+
writer.WriteStartObject();
30+
writer.WriteEndObject();
2831
}
2932

3033
internal override object ReadWithQuotes(ref Utf8JsonReader reader)

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonConverterOfT.cs

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -349,28 +349,14 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
349349
ignoreCyclesPopReference = true;
350350
}
351351

352-
if (state.NextValueCanBePolymorphic ||
353-
// only needed to handle the corner case of root-level values of runtime type `System.Object`
354-
// TODO: delegate `System.Object` runtime type serialization to ObjectConverter.
355-
TypeToConvert == JsonTypeInfo.ObjectType)
352+
if (state.NextValueCanBePolymorphic)
356353
{
357-
Debug.Assert(ConverterStrategy != ConverterStrategy.Value || TypeToConvert == JsonTypeInfo.ObjectType);
354+
Debug.Assert(IsInternalConverter);
355+
Debug.Assert(ConverterStrategy != ConverterStrategy.Value || this is Converters.ObjectConverter);
358356

359357
Type type = value.GetType();
360-
if (type == JsonTypeInfo.ObjectType)
361-
{
362-
writer.WriteStartObject();
363-
writer.WriteEndObject();
364-
365-
if (ignoreCyclesPopReference)
366-
{
367-
state.ReferenceResolver.PopReferenceForCycleDetection();
368-
}
369358

370-
return true;
371-
}
372-
373-
if (type != TypeToConvert && IsInternalConverter)
359+
if (type != TypeToConvert)
374360
{
375361
// For internal converter only: Handle polymorphic case and get the new converter.
376362
// Custom converter, even though polymorphic converter, get called for reading AND writing.
@@ -408,6 +394,15 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
408394
}
409395

410396
VerifyWrite(originalPropertyDepth, writer);
397+
398+
if (ignoreCyclesPopReference)
399+
{
400+
// should only be entered if we're serializing with the
401+
// internal object converter handling values of runtime type object.
402+
Debug.Assert(value?.GetType() == typeof(object) && this is Converters.ObjectConverter);
403+
state.ReferenceResolver.PopReferenceForCycleDetection();
404+
}
405+
411406
return true;
412407
}
413408

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfo.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,8 +551,9 @@ private void InitializePolymorphismConfiguration(ConverterStrategy converterStra
551551

552552
// Resolve polymorphic serialization configuration
553553
CanBeWritePolymorphic =
554+
isInternalConverter && !Type.IsValueType && !Type.IsSealed &&
554555
Type == JsonTypeInfo.ObjectType ||
555-
!Type.IsValueType && !Type.IsSealed && converterStrategy != ConverterStrategy.Value &&
556+
converterStrategy != ConverterStrategy.Value &&
556557
(Options?.SupportedPolymorphicTypes(Type) == true ||
557558
Type.GetCustomAttribute<JsonPolymorphicTypeAttribute>(inherit: false) is not null);
558559

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/CustomConverterTests/CustomConverterTests.Object.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,9 @@ public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonS
307307

308308
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
309309
{
310-
throw new InvalidOperationException("Should not get here.");
310+
Assert.IsType<object>(value);
311+
writer.WriteStartObject();
312+
writer.WriteEndObject();
311313
}
312314
}
313315

@@ -732,5 +734,34 @@ static void Verify(JsonSerializerOptions options)
732734
options.Converters.Add(new SystemObjectNewtonsoftCompatibleConverter());
733735
Verify(options);
734736
}
737+
738+
[Fact]
739+
public static void CanCustomizeSystemObjectSerialization()
740+
{
741+
var options = new JsonSerializerOptions { Converters = { new CustomSystemObjectConverter() } };
742+
743+
string expectedJson = "\"Object\"";
744+
string actualJson = JsonSerializer.Serialize(new object(), options);
745+
Assert.Equal(expectedJson, actualJson);
746+
}
747+
748+
private class CustomSystemObjectConverter : JsonConverter<object>
749+
{
750+
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => throw new NotImplementedException();
751+
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
752+
{
753+
if (value?.GetType() == typeof(object))
754+
{
755+
// check that serialization for values of
756+
// runtime type object can be customized.
757+
writer.WriteStringValue("Object");
758+
}
759+
else
760+
{
761+
throw new NotSupportedException();
762+
}
763+
}
764+
}
735765
}
766+
736767
}

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/OptionsTests.cs

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -328,12 +328,12 @@ public static void JsonEncodedTextStringsCustomAllowAll(string message, string e
328328
[Fact]
329329
public static void Options_GetConverterForObjectJsonElement_GivesCorrectConverter()
330330
{
331-
GenericObjectOrJsonElementConverterTestHelper<object>("ObjectConverter", new object(), "[3]", true);
331+
GenericObjectOrJsonElementConverterTestHelper<object>("ObjectConverter", new object(), "{}");
332332
JsonElement element = JsonDocument.Parse("[3]").RootElement;
333-
GenericObjectOrJsonElementConverterTestHelper<JsonElement>("JsonElementConverter", element, "[3]", false);
333+
GenericObjectOrJsonElementConverterTestHelper<JsonElement>("JsonElementConverter", element, "[3]");
334334
}
335335

336-
private static void GenericObjectOrJsonElementConverterTestHelper<T>(string converterName, object objectValue, string stringValue, bool throws)
336+
private static void GenericObjectOrJsonElementConverterTestHelper<T>(string converterName, object objectValue, string stringValue)
337337
{
338338
var options = new JsonSerializerOptions();
339339

@@ -347,10 +347,7 @@ private static void GenericObjectOrJsonElementConverterTestHelper<T>(string conv
347347

348348
if (readValue is JsonElement element)
349349
{
350-
Assert.Equal(JsonValueKind.Array, element.ValueKind);
351-
JsonElement.ArrayEnumerator iterator = element.EnumerateArray();
352-
Assert.True(iterator.MoveNext());
353-
Assert.Equal(3, iterator.Current.GetInt32());
350+
JsonTestHelper.AssertJsonEqual(stringValue, element.ToString());
354351
}
355352
else
356353
{
@@ -360,22 +357,14 @@ private static void GenericObjectOrJsonElementConverterTestHelper<T>(string conv
360357
using (var stream = new MemoryStream())
361358
using (var writer = new Utf8JsonWriter(stream))
362359
{
363-
if (throws)
364-
{
365-
Assert.Throws<InvalidOperationException>(() => converter.Write(writer, (T)objectValue, options));
366-
Assert.Throws<InvalidOperationException>(() => converter.Write(writer, (T)objectValue, null));
367-
}
368-
else
369-
{
370-
converter.Write(writer, (T)objectValue, options);
371-
writer.Flush();
372-
Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray()));
373-
374-
writer.Reset(stream);
375-
converter.Write(writer, (T)objectValue, null); // Test with null option
376-
writer.Flush();
377-
Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray()));
378-
}
360+
converter.Write(writer, (T)objectValue, options);
361+
writer.Flush();
362+
Assert.Equal(stringValue, Encoding.UTF8.GetString(stream.ToArray()));
363+
364+
writer.Reset(stream);
365+
converter.Write(writer, (T)objectValue, null); // Test with null option
366+
writer.Flush();
367+
Assert.Equal(stringValue + stringValue, Encoding.UTF8.GetString(stream.ToArray()));
379368
}
380369
}
381370

0 commit comments

Comments
 (0)