Skip to content

Commit 707cf4f

Browse files
Move System.Object serialization to ObjectConverter (#54436)
* move System.Object serialization to ObjectConverter * simply customized object converter test
1 parent 89603ec commit 707cf4f

File tree

5 files changed

+58
-38
lines changed

5 files changed

+58
-38
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: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,11 @@ public abstract partial class JsonConverter<T> : JsonConverter
1818
/// </summary>
1919
protected internal JsonConverter()
2020
{
21-
// Today only typeof(object) can have polymorphic writes.
22-
// In the future, this will be check for !IsSealed (and excluding value types).
23-
CanBePolymorphic = TypeToConvert == JsonTypeInfo.ObjectType;
21+
IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly;
22+
// Today only the internal JsonConverter<object> can have polymorphic writes.
23+
CanBePolymorphic = IsInternalConverter && TypeToConvert == JsonTypeInfo.ObjectType;
2424
IsValueType = TypeToConvert.IsValueType;
2525
CanBeNull = default(T) is null;
26-
IsInternalConverter = GetType().Assembly == typeof(JsonConverter).Assembly;
2726

2827
if (HandleNull)
2928
{
@@ -332,7 +331,7 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
332331
bool ignoreCyclesPopReference = false;
333332

334333
if (
335-
#if NET6_0_OR_GREATER
334+
#if NET5_0_OR_GREATER
336335
!typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
337336
#else
338337
!IsValueType &&
@@ -368,15 +367,11 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
368367

369368
if (CanBePolymorphic)
370369
{
370+
Debug.Assert(IsInternalConverter);
371+
371372
Type type = value.GetType();
372-
if (type == JsonTypeInfo.ObjectType)
373-
{
374-
writer.WriteStartObject();
375-
writer.WriteEndObject();
376-
return true;
377-
}
378373

379-
if (type != TypeToConvert && IsInternalConverter)
374+
if (type != TypeToConvert)
380375
{
381376
// For internal converter only: Handle polymorphic case and get the new converter.
382377
// Custom converter, even though polymorphic converter, get called for reading AND writing.
@@ -420,6 +415,19 @@ internal bool TryWrite(Utf8JsonWriter writer, in T value, JsonSerializerOptions
420415
}
421416

422417
VerifyWrite(originalPropertyDepth, writer);
418+
419+
if (
420+
#if NET5_0_OR_GREATER
421+
!typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
422+
#endif
423+
ignoreCyclesPopReference)
424+
{
425+
// should only be entered if we're serializing instances
426+
// of type object using the internal object converter.
427+
Debug.Assert(value?.GetType() == typeof(object) && IsInternalConverter);
428+
state.ReferenceResolver.PopReferenceForCycleDetection();
429+
}
430+
423431
return true;
424432
}
425433

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ internal override bool GetMemberAndWriteJson(object obj, ref WriteStack state, U
235235
T value = Get!(obj);
236236

237237
if (
238-
#if NET6_0_OR_GREATER
238+
#if NET5_0_OR_GREATER
239239
!typeof(T).IsValueType && // treated as a constant by recent versions of the JIT.
240240
#else
241241
!Converter.IsValueType &&

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

Lines changed: 21 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,23 @@ 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 = "42";
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+
=> writer.WriteNumberValue(42);
753+
}
735754
}
755+
736756
}

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)